diff --git a/.github/workflows/linux-build-and-test.yaml b/.github/workflows/linux-build-and-test.yaml index c3637ac972c..230915e2da0 100644 --- a/.github/workflows/linux-build-and-test.yaml +++ b/.github/workflows/linux-build-and-test.yaml @@ -104,8 +104,7 @@ jobs: run: | set -o pipefail if [[ "${{ matrix.pg }}" == "13.1" ]]; then - make -k -C build isolationcheck isolationcheck-t ${{ matrix.installcheck_args }} | tee installcheck.log - make -k -C build regresscheck | tee -a installcheck.log + make -k -C build isolationcheck isolationcheck-t regresscheck regresscheck-shared ${{ matrix.installcheck_args }} | tee installcheck.log else make -k -C build installcheck ${{ matrix.installcheck_args }} | tee installcheck.log fi diff --git a/tsl/test/shared/expected/constify_timestamptz_op_interval-13.out b/tsl/test/shared/expected/constify_timestamptz_op_interval-13.out new file mode 100644 index 00000000000..60f0b481811 --- /dev/null +++ b/tsl/test/shared/expected/constify_timestamptz_op_interval-13.out @@ -0,0 +1,268 @@ +-- 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 (costs off)' +-- we disable ChunkAppend and ConstraintAwareAppend here to make the exclusion easier to spot +-- otherwise those would remove the chunks from the plan during execution +SET timescaledb.enable_chunk_append TO FALSE; +SET timescaledb.enable_constraint_aware_append TO FALSE; +-- plan query on complete hypertable to get a list of the chunks +:PREFIX +SELECT time +FROM metrics; + QUERY PLAN +------------------------------------ + Append + -> Seq Scan on _hyper_1_1_chunk + -> Seq Scan on _hyper_1_2_chunk + -> Seq Scan on _hyper_1_3_chunk +(4 rows) + +-- all of these should have all chunk exclusion happening at plan time +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-01-01'::timestamptz - '6h'::interval; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-01-01'::timestamptz + '6h'::interval; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone + '@ 6 hours'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '6h'::interval + '2000-01-01'::timestamptz; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone + '@ 6 hours'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-01-07'::timestamptz - '7 day 8 seconds'::interval; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone - '@ 7 days 8 secs'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-03-01'::timestamptz - '60 day'::interval; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Wed Mar 01 00:00:00 2000 PST'::timestamp with time zone - '@ 60 days'::interval)) +(2 rows) + +-- test Var on right side of expression +:PREFIX +SELECT time +FROM metrics +WHERE '2000-01-01'::timestamptz - '6h'::interval > time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE '2000-01-07'::timestamptz - '7 day'::interval > time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------ + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone - '@ 7 days'::interval)) +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE '2000-03-01'::timestamptz - '60 day'::interval > time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Wed Mar 01 00:00:00 2000 PST'::timestamp with time zone - '@ 60 days'::interval)) +(2 rows) + +-- test multiple constraints +:PREFIX +SELECT time +FROM metrics +WHERE time > '2000-01-10'::timestamptz - '6h'::interval + AND time < '2000-01-10'::timestamptz + '6h'::interval; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: (("time" > ('Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) AND ("time" < ('Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone + '@ 6 hours'::interval))) +(2 rows) + +-- test on space-partitioned hypertable +:PREFIX +SELECT time +FROM metrics_space +WHERE time < '2000-01-01'::timestamptz - '6h'::interval + AND device_id = 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk + Index Cond: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) + Filter: (device_id = 1) +(3 rows) + +-- test on compressed hypertable +:PREFIX +SELECT time +FROM metrics_compressed +WHERE time < '2000-01-01'::timestamptz - '6h'::interval; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Custom Scan (DecompressChunk) on _hyper_3_13_chunk + Filter: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) + -> Seq Scan on compress_hyper_4_18_chunk + Filter: (_ts_meta_min_1 < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) +(4 rows) + +-- test on space-partitioned compressed hypertable +:PREFIX +SELECT time +FROM metrics_space_compressed +WHERE time < '2000-01-01'::timestamptz - '6h'::interval + AND device_id = 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (DecompressChunk) on _hyper_5_19_chunk + Filter: ("time" < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval)) + -> Seq Scan on compress_hyper_6_36_chunk + Filter: ((device_id = 1) AND (_ts_meta_min_1 < ('Sat Jan 01 00:00:00 2000 PST'::timestamp with time zone - '@ 6 hours'::interval))) +(4 rows) + +-- month/year intervals are not constified +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-02-01'::timestamptz - '1 month'::interval; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon'::interval)) +(7 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-02-01'::timestamptz - '1 month - 1 day'::interval; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon -1 days'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon -1 days'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon -1 days'::interval)) +(7 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-02-01'::timestamptz - '1 month + 1 day'::interval; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon 1 day'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon 1 day'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 mon 1 day'::interval)) +(7 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE '2000-02-01'::timestamptz - '1 year'::interval > time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 year'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 year'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" < ('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone - '@ 1 year'::interval)) +(7 rows) + +-- nested expressions are not constified +:PREFIX +SELECT time +FROM metrics +WHERE time < '1 day' + '2000-02-01'::timestamptz - '1 month'::interval; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------- + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" < (('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone + '@ 1 day'::interval) - '@ 1 mon'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" < (('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone + '@ 1 day'::interval) - '@ 1 mon'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" < (('Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone + '@ 1 day'::interval) - '@ 1 mon'::interval)) +(7 rows) + +-- non-Const expressions are not constified +:PREFIX +SELECT time +FROM metrics +WHERE time > now() - '6h'::interval; + QUERY PLAN +----------------------------------------------------------------------------------- + Append + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk + Index Cond: ("time" > (now() - '@ 6 hours'::interval)) + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk + Index Cond: ("time" > (now() - '@ 6 hours'::interval)) + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk + Index Cond: ("time" > (now() - '@ 6 hours'::interval)) +(7 rows) + +-- test NULL values +:PREFIX +SELECT time +FROM metrics +WHERE time < '2000-02-01'::timestamptz - NULL::interval; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +:PREFIX +SELECT time +FROM metrics +WHERE time < NULL::timestamptz - NULL::interval; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + diff --git a/tsl/test/shared/expected/constraint_exclusion_prepared.out b/tsl/test/shared/expected/constraint_exclusion_prepared.out index 7e7e16f05ba..58899e0cc89 100644 --- a/tsl/test/shared/expected/constraint_exclusion_prepared.out +++ b/tsl/test/shared/expected/constraint_exclusion_prepared.out @@ -13,6 +13,13 @@ SELECT format('\! diff -u --label "Uncompressed results" --label "Compressed res \set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)' \set PREFIX_VERBOSE 'EXPLAIN (analyze, costs off, timing off, summary off, verbose)' set work_mem to '64MB'; +-- disable incremental sort here to make plans comparable to PG < 13 +SELECT CASE WHEN current_setting('server_version_num')::int/10000 >= 13 THEN set_config('enable_incremental_sort','off',false) ELSE 'off' END; + case +------ + off +(1 row) + set max_parallel_workers_per_gather to 0; \set TEST_TABLE 'metrics' \ir :TEST_QUERY_NAME diff --git a/tsl/test/shared/expected/gapfill.out b/tsl/test/shared/expected/gapfill-11.out similarity index 100% rename from tsl/test/shared/expected/gapfill.out rename to tsl/test/shared/expected/gapfill-11.out diff --git a/tsl/test/shared/expected/gapfill-12.out b/tsl/test/shared/expected/gapfill-12.out new file mode 100644 index 00000000000..36aad6363d8 --- /dev/null +++ b/tsl/test/shared/expected/gapfill-12.out @@ -0,0 +1,3117 @@ +-- 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 EXPLAIN 'EXPLAIN (COSTS OFF)' +-- simple example +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test sorting +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (avg("*VALUES*".column2)) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test sort direction +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1 DESC; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) DESC + -> Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) NULLS FIRST + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now()) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test order by aggregate function +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2,1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (avg("*VALUES*".column2)), (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test query without order by +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test parallel query +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + avg(value) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- test parallel query with locf +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- test parallel query with interpolate +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- make sure we can run gapfill in parallel workers +-- ensure this plan runs in parallel +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit + -> Sort + Sort Key: (interpolate(avg(value), NULL::record, NULL::record)) + -> Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(18 rows) + +-- actually run a parallel gapfill +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + time_bucket_gapfill | interpolate +------------------------------+------------- + Mon Jan 01 00:00:00 2018 PST | 1 +(1 row) + +-- test sort optimizations +-- test sort optimization with single member order by, +-- should use index scan (no GapFill node for this one since we're not gapfilling) +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on gapfill_plan_test + Order: time_bucket_gapfill('@ 5 mins'::interval, gapfill_plan_test."time", NULL::timestamp with time zone, NULL::timestamp with time zone) + -> Index Scan Backward using _hyper_8_46_chunk_gapfill_plan_test_time_idx on _hyper_8_46_chunk + -> Index Scan Backward using _hyper_8_47_chunk_gapfill_plan_test_time_idx on _hyper_8_47_chunk + -> Index Scan Backward using _hyper_8_48_chunk_gapfill_plan_test_time_idx on _hyper_8_48_chunk + -> Index Scan Backward using _hyper_8_49_chunk_gapfill_plan_test_time_idx on _hyper_8_49_chunk +(6 rows) + +SET max_parallel_workers_per_gather TO 0; +-- test sort optimizations with locf +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, gapfill_plan_test."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Custom Scan (ChunkAppend) on gapfill_plan_test + Order: time_bucket_gapfill('@ 5 mins'::interval, gapfill_plan_test."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Index Scan Backward using _hyper_8_46_chunk_gapfill_plan_test_time_idx on _hyper_8_46_chunk + -> Index Scan Backward using _hyper_8_47_chunk_gapfill_plan_test_time_idx on _hyper_8_47_chunk + -> Index Scan Backward using _hyper_8_48_chunk_gapfill_plan_test_time_idx on _hyper_8_48_chunk + -> Index Scan Backward using _hyper_8_49_chunk_gapfill_plan_test_time_idx on _hyper_8_49_chunk +(9 rows) + +-- test sort optimizations with interpolate +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, gapfill_plan_test."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Custom Scan (ChunkAppend) on gapfill_plan_test + Order: time_bucket_gapfill('@ 5 mins'::interval, gapfill_plan_test."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Index Scan Backward using _hyper_8_46_chunk_gapfill_plan_test_time_idx on _hyper_8_46_chunk + -> Index Scan Backward using _hyper_8_47_chunk_gapfill_plan_test_time_idx on _hyper_8_47_chunk + -> Index Scan Backward using _hyper_8_48_chunk_gapfill_plan_test_time_idx on _hyper_8_48_chunk + -> Index Scan Backward using _hyper_8_49_chunk_gapfill_plan_test_time_idx on _hyper_8_49_chunk +(9 rows) + +RESET max_parallel_workers_per_gather; +CREATE INDEX gapfill_plan_test_indx ON gapfill_plan_test(value, time); +-- test sort optimization with ordering by multiple columns and time_bucket_gapfill not last, +-- must not use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1,2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)), _hyper_8_46_chunk.value + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(8 rows) + +-- test sort optimization with ordering by multiple columns and time_bucket as last member, +-- should use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 2,1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Result + -> Merge Append + Sort Key: _hyper_8_46_chunk.value, (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Index Only Scan using _hyper_8_46_chunk_gapfill_plan_test_indx on _hyper_8_46_chunk + -> Index Only Scan using _hyper_8_47_chunk_gapfill_plan_test_indx on _hyper_8_47_chunk + -> Index Only Scan using _hyper_8_48_chunk_gapfill_plan_test_indx on _hyper_8_48_chunk + -> Index Only Scan using _hyper_8_49_chunk_gapfill_plan_test_indx on _hyper_8_49_chunk +(7 rows) + +\set METRICS metrics_int +-- All test against table :METRICS first +\set ON_ERROR_STOP 0 +-- inverse of previous test query to confirm an error is actually thrown +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time = 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; +ERROR: division by zero +-- test window functions with multiple column references +SELECT + time_bucket_gapfill(1,time,1,2), + first(min(time),min(time)) OVER () +FROM :METRICS +GROUP BY 1; +ERROR: window functions with multiple column references not supported +-- test with unsupported operator +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time =0 AND time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test with 2 tables and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test inner join and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time +WHERE m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test outer join with constraints in join condition +-- not usable as start/stop +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 LEFT OUTER JOIN :METRICS m2 ON m1.time=m2.time AND m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +\set ON_ERROR_STOP 1 +\ir include/gapfill_metrics_query.sql +-- 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. +-- test locf lookup query does not trigger when not needed +-- +-- 1/(SELECT 0) will throw an error in the lookup query but in order to not +-- always trigger evaluation it needs to be correlated otherwise postgres will +-- always run it once even if the value is never used +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | locf3 +------+-----------+-----------+------- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +-- test locf with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value)::int,23) AS locf1, + locf(min(value)::int,(SELECT 42)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and "wrong order" +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value),23::float) AS locf1, + locf(min(value),(SELECT 42::float)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 1,2,3; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and window functions +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)), + sum(locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1))) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3; + time | device_id | sensor_id | locf | sum +------+-----------+-----------+------+------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 5 | 10 + 10 | 1 | 1 | 5 | 10 + 0 | 1 | 2 | -100 | -100 + 5 | 1 | 2 | 10 | -90 + 10 | 1 | 2 | 10 | 20 +(6 rows) + +-- test JOINs +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id, + d.name, + sensor_id, + s.name, + avg(m.value) +FROM :METRICS m +INNER JOIN devices d USING(device_id) +INNER JOIN sensors s USING(sensor_id) +WHERE time BETWEEN 0 AND 5 +GROUP BY 1,2,3,4,5; + time | device_id | name | sensor_id | name | avg +------+-----------+----------+-----------+----------+----- + 0 | 1 | Device 1 | 1 | Sensor 1 | 5 + 1 | 1 | Device 1 | 1 | Sensor 1 | + 2 | 1 | Device 1 | 1 | Sensor 1 | + 3 | 1 | Device 1 | 1 | Sensor 1 | + 4 | 1 | Device 1 | 1 | Sensor 1 | + 0 | 1 | Device 1 | 2 | Sensor 2 | + 1 | 1 | Device 1 | 2 | Sensor 2 | + 2 | 1 | Device 1 | 2 | Sensor 2 | + 3 | 1 | Device 1 | 2 | Sensor 2 | + 4 | 1 | Device 1 | 2 | Sensor 2 | + 5 | 1 | Device 1 | 2 | Sensor 2 | 10 +(11 rows) + +-- test interpolate with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + interpolate(min(value)) AS ip, + interpolate(min(value),(-5,-5.0::float),(15,20.0::float)) AS ip1, + interpolate(min(value),(SELECT (-10,-10.0::float)),(SELECT (15,20.0::float))) AS ip2, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) AS ip3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | ip | ip1 | ip2 | ip3 +------+-----------+-----------+-----+----+-----+------------------+------------------ + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | | 10 | 10 | 4.75 + 10 | 1 | 1 | | | 15 | 15 | 4.5 + 0 | 1 | 2 | | | 2.5 | 3.33333333333333 | 4.76190476190476 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | | 15 | 15 | 4.21052631578947 +(6 rows) + +-- test interpolate with correlated subquery and window function +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ), + sum(interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + )) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | interpolate | sum +------+-----------+-----------+------------------+------------------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 4.75 | 9.75 + 10 | 1 | 1 | 4.5 | 9.25 + 0 | 1 | 2 | 4.76190476190476 | 4.76190476190476 + 5 | 1 | 2 | 10 | 14.7619047619048 + 10 | 1 | 2 | 4.21052631578947 | 14.2105263157895 +(6 rows) + +-- test subqueries +-- subqueries will alter the shape of the plan and top-level constraints +-- might not end up in top-level of jointree +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 +WHERE m1.time >=0 AND m1.time < 2 AND device_id IN (SELECT device_id FROM :METRICS) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test inner join with constraints in join condition +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time AND m2.time >=0 AND m2.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test actual table +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time >=0 AND time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test with table alias +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS m +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test with 2 tables +SELECT + time_bucket_gapfill(1,m.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test prepared statement with locf with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with interpolate with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with variable gapfill arguments +PREPARE prep_gapfill(int,int,int) AS +SELECT + time_bucket_gapfill($1,time,$2,$3) AS time, + device_id, + sensor_id, + min(value) +FROM :METRICS m1 +WHERE time >= $2 AND time < $3 AND device_id=1 AND sensor_id=1 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +DEALLOCATE prep_gapfill; +-- Tests without tables +-- test locf and interpolate call without gapfill +SELECT locf(1); + locf +------ + 1 +(1 row) + +SELECT interpolate(1); + interpolate +------------- + 1 +(1 row) + +-- test locf and interpolate call with NULL input +SELECT locf(NULL::int); + locf +------ + +(1 row) + +SELECT interpolate(NULL); + interpolate +------------- + +(1 row) + +\set ON_ERROR_STOP 0 +-- test time_bucket_gapfill not top level function call +SELECT + 1 + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: no top level time_bucket_gapfill in group by clause +-- test locf with treat_null_as_missing not BOOL +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=1) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function locf(integer, treat_null_as_missing => integer) does not exist at character 46 +-- test locf with treat_null_as_missing not literal +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=random()>0) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid locf argument: treat_null_as_missing must be a BOOL literal +-- test interpolate lookup query with 1 element in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT ROW(10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT ROW(10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with 3 elements in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with mismatching time datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10::float,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10::float,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +-- test interpolate lookup query with mismatching value datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10::float))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10::float))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +-- test interpolate with unsupported datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(text 'text') +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function interpolate(text) does not exist at character 46 +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interval '1d') +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: function interpolate(interval) does not exist at character 46 +-- test multiple time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time,1,11),time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time_bucket_gapfill(1,time,1,11),1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested locf calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(locf(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test nested interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test mixed locf/interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test window function inside locf +SELECT + time_bucket_gapfill(1,time,1,11), + locf(avg(min(time)) OVER ()) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window functions must not be below locf +-- test nested window functions +-- prevented by postgres +SELECT + time_bucket_gapfill(1,time,1,11), + avg(avg(min(time)) OVER ()) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window function calls cannot be nested at character 50 +-- test multiple window functions in single column +SELECT + time_bucket_gapfill(1,time,1,11), + avg(min(time)) OVER () + avg(min(time)) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple window function calls per column not supported +-- test locf not toplevel +SELECT + time_bucket_gapfill(1,time,1,11), + 1 + locf(min(time)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: locf must be toplevel function call +-- test locf inside aggregate +SELECT + time_bucket_gapfill(1,time,1,11), + min(min(locf(time))) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: aggregate functions must be below locf +-- test NULL args +SELECT + time_bucket_gapfill(NULL,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill(1,NULL,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill(1,time,NULL,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +SELECT + time_bucket_gapfill(1,time,1,NULL) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- test 0 bucket_width +SELECT + time_bucket_gapfill(0,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test negative bucket_width +SELECT + time_bucket_gapfill(-1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test subqueries as interval, start and stop (not supported atm) +SELECT + time_bucket_gapfill((SELECT 1),time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +SELECT + time_bucket_gapfill(1,time,(SELECT 1),11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +SELECT + time_bucket_gapfill(1,time,1,(SELECT 11)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +\set ON_ERROR_STOP 1 +-- test time_bucket_gapfill without aggregation +-- this will not trigger gapfilling +SELECT + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill +--------------------- + 1 + 2 +(2 rows) + +SELECT + time_bucket_gapfill(1,time,1,11), + avg(time) OVER () +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill | avg +---------------------+-------------------- + 1 | 1.5000000000000000 + 2 | 1.5000000000000000 +(2 rows) + +-- test int int2/4/8 +SELECT + time_bucket_gapfill(1::int2,time::int2,0::int2,6::int2) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int4,time::int4,0::int4,6::int4) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int8,time::int8,0::int8,6::int8) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +-- test non-aligned bucket start +SELECT + time_bucket_gapfill(10,time,5,40) +FROM (VALUES (11),(22)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 10 + 20 + 30 +(4 rows) + +-- simple gapfill query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + min(value) AS value +FROM (values (-10,1),(10,2),(11,3),(12,4),(22,5),(30,6),(66,7)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + -10 | 1 + 0 | + 10 | 2 + 20 | 5 + 30 | 6 + 40 | + 60 | 7 +(7 rows) + +-- test references to different columns +SELECT + time_bucket_gapfill(1,t,0,5) as t, + min(t),max(t),min(v),max(v) +FROM(VALUES (1,3),(2,5)) tb(t,v) +GROUP BY 1 ORDER BY 1; + t | min | max | min | max +---+-----+-----+-----+----- + 0 | | | | + 1 | 1 | 1 | 3 | 3 + 2 | 2 | 2 | 5 | 5 + 3 | | | | + 4 | | | | +(5 rows) + +-- test passing of values outside boundaries +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (-1),(1),(3),(6)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + -1 | -1 + 0 | + 1 | 1 + 2 | + 3 | 3 + 4 | + 6 | 6 +(7 rows) + +-- test gap fill before first row and after last row +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | 1 + 2 | 2 + 3 | 3 + 4 | +(5 rows) + +-- test gap fill without rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +WHERE false +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | + 2 | + 3 | + 4 | +(5 rows) + +-- test coalesce +SELECT + time_bucket_gapfill(1,time,0,5), + coalesce(min(time),0), + coalesce(min(value),0), + coalesce(min(value),7) +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | coalesce | coalesce | coalesce +---------------------+----------+----------+---------- + 0 | 0 | 0 | 7 + 1 | 1 | 1 | 1 + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 0 | 0 | 7 +(5 rows) + +-- test case +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + CASE WHEN min(time) IS NOT NULL THEN min(time) ELSE -1 END, + CASE WHEN min(time) IS NOT NULL THEN min(time) + 7 ELSE 0 END, + CASE WHEN 1 = 1 THEN 1 ELSE 0 END +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | case | case | case +---------------------+-----+------+------+------ + 0 | | -1 | 0 | 1 + 1 | 1 | 1 | 8 | 1 + 2 | 2 | 2 | 9 | 1 + 3 | 3 | 3 | 10 | 1 + 4 | | -1 | 0 | 1 +(5 rows) + +-- test constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), min(time), 4 as c +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | min | c +---------------------+-----+-----+--- + 0 | | | 4 + 1 | 1 | 1 | 4 + 2 | 2 | 2 | 4 + 3 | 3 | 3 | 4 + 4 | | | 4 +(5 rows) + +-- test column reordering +SELECT + 1 as c1, '2' as c2, + time_bucket_gapfill(1,time,0,5), + 3.0 as c3, + min(time), min(time), 4 as c4 +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 3 ORDER BY 3; + c1 | c2 | time_bucket_gapfill | c3 | min | min | c4 +----+----+---------------------+-----+-----+-----+---- + 1 | 2 | 0 | 3.0 | | | 4 + 1 | 2 | 1 | 3.0 | 1 | 1 | 4 + 1 | 2 | 2 | 3.0 | 2 | 2 | 4 + 1 | 2 | 3 | 3.0 | 3 | 3 | 4 + 1 | 2 | 4 | 3.0 | | | 4 +(5 rows) + +-- test timestamptz +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMPTZ '2000-01-01',TIMESTAMPTZ '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMPTZ '2000-01-01 9:00:00'),(TIMESTAMPTZ '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +------------------------------+------------------------------ + Fri Dec 31 22:00:00 1999 PST | + Sat Jan 01 04:00:00 2000 PST | Sat Jan 01 09:00:00 2000 PST + Sat Jan 01 10:00:00 2000 PST | + Sat Jan 01 16:00:00 2000 PST | Sat Jan 01 18:00:00 2000 PST + Sat Jan 01 22:00:00 2000 PST | +(5 rows) + +-- test timestamp +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMP '2000-01-01',TIMESTAMP '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMP '2000-01-01 9:00:00'),(TIMESTAMP '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +--------------------------+-------------------------- + Sat Jan 01 00:00:00 2000 | + Sat Jan 01 06:00:00 2000 | Sat Jan 01 09:00:00 2000 + Sat Jan 01 12:00:00 2000 | + Sat Jan 01 18:00:00 2000 | Sat Jan 01 18:00:00 2000 +(4 rows) + +-- test date +SELECT + time_bucket_gapfill(INTERVAL '1w',time,DATE '2000-01-01',DATE '2000-02-10'), + min(time) +FROM (VALUES (DATE '2000-01-08'),(DATE '2000-01-22')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+------------ + 12-27-1999 | + 01-03-2000 | 01-08-2000 + 01-10-2000 | + 01-17-2000 | 01-22-2000 + 01-24-2000 | + 01-31-2000 | + 02-07-2000 | +(7 rows) + +-- test grouping by non-time columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test grouping by non-time columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +WHERE false +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- +(0 rows) + +-- test duplicate columns in GROUP BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,2,3 ORDER BY 2,1; + time | id | id | m +------+----+----+--- + 0 | 1 | 1 | + 1 | 1 | 1 | 1 + 2 | 1 | 1 | + 3 | 1 | 1 | + 4 | 1 | 1 | + 0 | 2 | 2 | + 1 | 2 | 2 | + 2 | 2 | 2 | 2 + 3 | 2 | 2 | + 4 | 2 | 2 | +(10 rows) + +-- test grouping by columns not in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY id,1; + time | m +------+--- + 0 | + 1 | 1 + 2 | + 3 | + 4 | + 0 | + 1 | + 2 | 2 + 3 | + 4 | +(10 rows) + +-- test grouping by non-time columns with text columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 1 | blue | 1 + 2 | blue | + 3 | blue | + 4 | blue | + 0 | red | + 1 | red | + 2 | red | 2 + 3 | red | + 4 | red | +(10 rows) + +-- test grouping by non-time columns with text columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +WHERE false +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- +(0 rows) + +-- test insert into SELECT +SELECT * FROM insert_test; + id +---- + 1 + 2 + 3 + 4 +(4 rows) + +-- test join +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (1,'red',1),(2,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | m | m +------+-------+---+--- + 0 | blue | | + 1 | blue | | + 2 | blue | 2 | + 3 | blue | | + 4 | blue | | 2 + 0 | red | | + 1 | red | 1 | + 2 | red | | + 3 | red | | 1 + 4 | red | | +(10 rows) + +-- test join with locf +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as locf + FROM + (VALUES (0,'red',1),(0,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | locf | m +------+-------+------+--- + 0 | blue | 2 | + 1 | blue | 2 | + 2 | blue | 2 | + 3 | blue | 2 | + 4 | blue | 2 | 2 + 0 | red | 1 | + 1 | red | 1 | + 2 | red | 1 | + 3 | red | 1 | 1 + 4 | red | 1 | 1 +(10 rows) + +-- test locf +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=NULL) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in first row of resultset and treat_null_as_missing with lookup query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false, prev := (SELECT 100)) AS v1, + locf(min(value),treat_null_as_missing:=true, prev := (SELECT 100)) AS v2 +FROM (values (0,NULL),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | v1 | v2 +------+----+----- + 0 | | 100 + 10 | | 100 + 20 | | 100 + 30 | | 100 + 40 | | 100 + 50 | 6 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing with resort +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1 DESC; + time | value +------+------- + 50 | 6 + 40 | 3 + 30 | 3 + 20 | 3 + 10 | 9 + 0 | +(6 rows) + +-- test locf with constants +SELECT + time_bucket_gapfill(1,time,0,5), + 2, + locf(min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | ?column? | locf +---------------------+----------+------ + 0 | 2 | 1 + 1 | 2 | 1 + 2 | 2 | 1 + 3 | 2 | 1 + 4 | 2 | 2 +(5 rows) + +-- test expressions inside locf +SELECT + time_bucket_gapfill(1,time,0,5), + locf(min(value)), + locf(4), + locf(4 + min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | locf | locf | locf +---------------------+------+------+------ + 0 | 1 | 4 | 5 + 1 | 1 | 4 | 5 + 2 | 1 | 4 | 5 + 3 | 1 | 4 | 5 + 4 | 2 | 4 | 6 +(5 rows) + +-- test locf with out of boundary lookup +SELECT + time_bucket_gapfill(10,time,0,70) AS time, + locf(min(value),(SELECT 100)) AS value +FROM (values (20,9),(40,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 100 + 10 | 100 + 20 | 9 + 30 | 9 + 40 | 6 + 50 | 6 + 60 | 6 +(7 rows) + +-- test locf with different datatypes +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1)) AS text, + locf(min(v2)) AS "int[]", + locf(min(v3)) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test locf with different datatypes and treat_null_as_missing +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1),treat_null_as_missing:=true) AS text, + locf(min(v2),treat_null_as_missing:=true) AS "int[]", + locf(min(v3),treat_null_as_missing:=true) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (2,NULL,NULL,NULL), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test interpolate +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(value)) AS value +FROM (values (0,1),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 1 + 10 | 2 + 20 | 3 + 30 | 4 + 40 | 5 + 50 | 6 +(6 rows) + +-- test interpolate with NULL values +SELECT + time_bucket_gapfill(1,time,0,5) AS time, + interpolate(avg(temp)) AS temp +FROM (VALUES (0,0),(2,NULL),(5,5)) v(time,temp) +GROUP BY 1; + time | temp +------+------ + 0 | 0 + 1 | + 2 | + 3 | + 4 | + 5 | 5 +(6 rows) + +-- test interpolate datatypes +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (0,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(50,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + 0 | -3 | -3 | -3 | -3 | -3 + 10 | -2 | -2 | -2 | -1.8 | -1.8 + 20 | -1 | -1 | -1 | -0.6 | -0.6 + 30 | 1 | 1 | 1 | 0.6 | 0.6 + 40 | 2 | 2 | 2 | 1.8 | 1.8 + 50 | 3 | 3 | 3 | 3 | 3 +(6 rows) + +-- test interpolate datatypes with negative time +SELECT + time_bucket_gapfill(10,time,-40,30) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (-40,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(20,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + -40 | -3 | -3 | -3 | -3 | -3 + -30 | -2 | -2 | -2 | -2 | -2 + -20 | -1 | -1 | -1 | -1 | -1 + -10 | 0 | 0 | 0 | 0 | 0 + 0 | 1 | 1 | 1 | 1 | 1 + 10 | 2 | 2 | 2 | 2 | 2 + 20 | 3 | 3 | 3 | 3 | 3 +(7 rows) + +-- test interpolate with multiple groupings +SELECT + time_bucket_gapfill(5,time,0,11), + device, + interpolate(min(v1),(SELECT (-10,-10)),(SELECT (20,10))) +FROM (VALUES (5,1,0),(5,2,0)) as v(time,device,v1) +GROUP BY 1,2 ORDER BY 2,1; + time_bucket_gapfill | device | interpolate +---------------------+--------+------------- + 0 | 1 | -3 + 5 | 1 | 0 + 10 | 1 | 3 + 0 | 2 | -3 + 5 | 2 | 0 + 10 | 2 | 3 +(6 rows) + +-- test cte with gap filling in outer query +WITH data AS ( + SELECT * FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +) +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM data +GROUP BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test cte with gap filling in inner query +WITH gapfill AS ( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m + FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) + GROUP BY 1,id +) +SELECT * FROM gapfill; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test window functions +SELECT + time_bucket_gapfill(10,time,0,60), + interpolate(min(time)), + lag(min(time)) OVER () +FROM (VALUES (0),(50)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | lag +---------------------+-------------+----- + 0 | 0 | + 10 | 10 | 0 + 20 | 20 | + 30 | 30 | + 40 | 40 | + 50 | 50 | +(6 rows) + +-- test window functions with multiple windows +SELECT + time_bucket_gapfill(1,time,0,10), + interpolate(min(time)), + row_number() OVER (), + locf(min(time)), + sum(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 2 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 3 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 4 PRECEDING) +FROM (VALUES (0),(9)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | row_number | locf | sum | sum | sum | sum +---------------------+-------------+------------+------+-----+-----+-----+----- + 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + 1 | 1 | 2 | 0 | 1 | 1 | 1 | 1 + 2 | 2 | 3 | 0 | 3 | 3 | 3 | 3 + 3 | 3 | 4 | 0 | 5 | 6 | 6 | 6 + 4 | 4 | 5 | 0 | 7 | 9 | 10 | 10 + 5 | 5 | 6 | 0 | 9 | 12 | 14 | 15 + 6 | 6 | 7 | 0 | 11 | 15 | 18 | 20 + 7 | 7 | 8 | 0 | 13 | 18 | 22 | 25 + 8 | 8 | 9 | 0 | 15 | 21 | 26 | 30 + 9 | 9 | 10 | 9 | 17 | 24 | 30 | 35 +(10 rows) + +-- test window functions with constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + 4 as c, + lag(min(time)) OVER () +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | c | lag +---------------------+-----+---+----- + 0 | | 4 | + 1 | 1 | 4 | + 2 | 2 | 4 | 1 + 3 | 3 | 4 | 2 + 4 | | 4 | 3 +(5 rows) + +--test window functions with locf +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + locf(min(time)) AS locf, + lag(locf(min(time))) over () AS lag_locf, + lead(locf(min(time))) over () AS lead_locf +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | locf | lag_locf | lead_locf +---------------------+-----+---------+----------+------+----------+----------- + 0 | | | 1 | | | 1 + 1 | 1 | | 2 | 1 | | 2 + 2 | 2 | 1 | | 2 | 1 | 2 + 3 | | 2 | | 2 | 2 | 2 + 4 | | | | 2 | 2 | +(5 rows) + +--test window functions with interpolate +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + lead(interpolate(min(time))) over () AS lead_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | interpolate | lag_interpolate | lead_interpolate +---------------------+-----+---------+----------+-------------+-----------------+------------------ + 0 | | | 1 | | | 1 + 1 | 1 | | | 1 | | 2 + 2 | | 1 | 3 | 2 | 1 | 3 + 3 | 3 | | | 3 | 2 | + 4 | | 3 | | | 3 | +(5 rows) + +--test window functions with expressions +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + 1 + lag(min(time)) over () AS lag_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + 1 + lag(interpolate(min(time))) over () AS lag_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lag_min | interpolate | lag_interpolate | lag_interpolate +---------------------+-----+---------+---------+-------------+-----------------+----------------- + 0 | | | | | | + 1 | 1 | | | 1 | | + 2 | | 1 | 2 | 2 | 1 | 2 + 3 | 3 | | | 3 | 2 | 3 + 4 | | 3 | 4 | | 3 | 4 +(5 rows) + +--test row_number/rank/percent_rank/... window functions with gapfill reference +SELECT + time_bucket_gapfill(1,time,0,5), + ntile(2) OVER () AS ntile_2, + ntile(3) OVER () AS ntile_3, + ntile(5) OVER () AS ntile_5, + row_number() OVER (), + cume_dist() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + rank() OVER (), + rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + percent_rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)) +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | ntile_2 | ntile_3 | ntile_5 | row_number | cume_dist | rank | rank | percent_rank +---------------------+---------+---------+---------+------------+-----------+------+------+-------------- + 0 | 1 | 1 | 1 | 1 | 0.2 | 1 | 1 | 0 + 1 | 1 | 1 | 2 | 2 | 0.4 | 1 | 2 | 0.25 + 2 | 1 | 2 | 3 | 3 | 0.6 | 1 | 3 | 0.5 + 3 | 2 | 2 | 4 | 4 | 0.8 | 1 | 4 | 0.75 + 4 | 2 | 3 | 5 | 5 | 1 | 1 | 5 | 1 +(5 rows) + +-- test first_value/last_value/nth_value +SELECT + time_bucket_gapfill(1,time,0,5), + first_value(min(time)) OVER (), + nth_value(min(time),3) OVER (), + last_value(min(time)) OVER () +FROM (VALUES (0),(2),(5)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | nth_value | last_value +---------------------+-------------+-----------+------------ + 0 | 0 | 2 | 5 + 1 | 0 | 2 | 5 + 2 | 0 | 2 | 5 + 3 | 0 | 2 | 5 + 4 | 0 | 2 | 5 + 5 | 0 | 2 | 5 +(6 rows) + +-- test window functions with PARTITION BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (), + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | row_number | row_number +------+-------+------------+------------ + 0 | blue | 1 | 1 + 1 | blue | 2 | 2 + 2 | blue | 3 | 3 + 3 | blue | 4 | 4 + 4 | blue | 5 | 5 + 0 | red | 6 | 1 + 1 | red | 7 | 2 + 2 | red | 8 | 3 + 3 | red | 9 | 4 + 4 | red | 10 | 5 +(10 rows) + +-- test multiple windows +\set ON_ERROR_STOP 0 +SELECT + time_bucket_gapfill(1,time,0,11), + first_value(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + interpolate(min(time)), + last_value(interpolate(min(time))) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +FROM (VALUES (0),(10)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | interpolate | last_value +---------------------+-------------+-------------+------------ + 0 | 0 | 0 | 1 + 1 | 0 | 1 | 2 + 2 | 1 | 2 | 3 + 3 | 2 | 3 | 4 + 4 | 3 | 4 | 5 + 5 | 4 | 5 | 6 + 6 | 5 | 6 | 7 + 7 | 6 | 7 | 8 + 8 | 7 | 8 | 9 + 9 | 8 | 9 | 10 + 10 | 9 | 10 | 10 +(11 rows) + +-- test reorder +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM + (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 0 | 2 | + 1 | 1 | 1 + 1 | 2 | + 2 | 1 | + 2 | 2 | 2 + 3 | 1 | + 3 | 2 | + 4 | 1 | + 4 | 2 | +(10 rows) + +-- test order by locf +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | locf +---------------------+------ + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 + 1 | +(5 rows) + +-- test order by interpolate +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 4 | + 5 | + 1 | 1 + 2 | 2 + 3 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +-- test queries on hypertable +-- test locf and interpolate together +SELECT + time_bucket_gapfill(interval '1h',time,timestamptz '2018-01-01 05:00:00-8', timestamptz '2018-01-01 07:00:00-8'), + device_id, + locf(avg(v1)) AS locf_v1, + locf(min(v2)) AS locf_v2, + interpolate(avg(v1)) AS interpolate_v1, + interpolate(avg(v2)) AS interpolate_v2 +FROM metrics_tstz +GROUP BY 1,2 +ORDER BY 1,2; + time_bucket_gapfill | device_id | locf_v1 | locf_v2 | interpolate_v1 | interpolate_v2 +------------------------------+-----------+---------+---------+----------------+---------------- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.5 | 10 | 0.25 | 5 + Mon Jan 01 06:00:00 2018 PST | 2 | 0.7 | 20 | 1.05 | 30 + Mon Jan 01 06:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 +(9 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz) < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +-- interpolation with correlated subquery lookup before interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 3:00 PST'::timestamptz, '2018-01-01 8:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + (SELECT (time,0.5::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time DESC LIMIT 1) + ), + avg(v1) +FROM metrics_tstz m1 +WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 03:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 04:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 +(5 rows) + +-- interpolation with correlated subquery lookup after interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 5:00 PST'::timestamptz, '2018-01-01 9:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + next=>(SELECT (time,v2::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time LIMIT 1) + ),avg(v1) +FROM metrics_tstz m1 WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 08:00:00 2018 PST | 1 | -5 | +(4 rows) + +\set ON_ERROR_STOP 0 +-- bucket_width non simple expression +SELECT + time_bucket_gapfill(t,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +-- no start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- no start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,finish:=1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- unsupported start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +-- NULL start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- unsupported finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +-- no finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- NULL finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- expression with column reference on right side +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE t > t AND t < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- expression with cast +SELECT + time_bucket_gapfill(1,t1::int8) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 >= 1 AND t1 <= 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +-- expression with multiple column references +SELECT + time_bucket_gapfill(1,t1+t2) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 1 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +-- expression with NULL start in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END AND t1 < 4 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- expression with NULL finish in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- non-Const NULL as start argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- non-Const NULL as finish argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,NULL,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- time_bucket_gapfill with constraints ORed +SELECT + time_bucket_gapfill(1::int8,t::int8) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 OR t < 3 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +\set ON_ERROR_STOP 1 +-- int32 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- same query with less or equal as finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t <= 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 + 3 +(5 rows) + +-- int32 time_bucket_gapfill with start column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + -1 < t AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 +(3 rows) + +-- int32 time_bucket_gapfill with finish column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= 0 AND 3 >= t +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 +(4 rows) + +-- int16 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int2,t) +FROM (VALUES (1::int2),(2::int2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- int64 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int8,t) +FROM (VALUES (1::int8),(2::int8)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- date time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('1d'::interval,t) +FROM (VALUES ('1999-12-30'::date),('2000-01-01'::date)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +--------------------- + 12-29-1999 + 12-30-1999 + 12-31-1999 + 01-01-2000 + 01-02-2000 +(5 rows) + +-- timestamp time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamp),('2000-01-01'::timestamp)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +-------------------------- + Wed Dec 29 00:00:00 1999 + Wed Dec 29 12:00:00 1999 + Thu Dec 30 00:00:00 1999 + Thu Dec 30 12:00:00 1999 + Fri Dec 31 00:00:00 1999 + Fri Dec 31 12:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sat Jan 01 12:00:00 2000 + Sun Jan 02 00:00:00 2000 + Sun Jan 02 12:00:00 2000 +(10 rows) + +-- timestamptz time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Tue Dec 28 16:00:00 1999 PST + Wed Dec 29 04:00:00 1999 PST + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(11 rows) + +-- timestamptz time_bucket_gapfill with more complex expression +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- timestamptz time_bucket_gapfill with different datatype in finish constraint +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03'::date +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- time_bucket_gapfill with now() as start +SELECT + time_bucket_gapfill('1h'::interval,t) +FROM (VALUES (now()),(now())) v(t) +WHERE + t >= now() AND t < now() - '1h'::interval +GROUP BY 1; + time_bucket_gapfill +--------------------- +(0 rows) + +-- time_bucket_gapfill with multiple constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 and t>1 AND t <=4 AND length(version()) > 0 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 2 +(1 row) + +-- int32 time_bucket_gapfill with greater for start +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t > -2 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- test DISTINCT +SELECT DISTINCT ON (color) + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 0 | red | +(2 rows) + +-- test DISTINCT with window functions +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER () +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 6 + 1 | red | 7 + 2 | red | 8 + 3 | red | 9 + 4 | red | 10 +(10 rows) + +-- test DISTINCT with window functions and PARTITION BY +SELECT DISTINCT ON (color,row_number() OVER (PARTITION BY color)) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test DISTINCT with window functions not in targetlist +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test column references +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test with Nested Loop +SELECT l.id, bucket, data_value FROM + (VALUES (1), (2), (3), (4)) a(id) + INNER JOIN LATERAL ( + SELECT b.id id, time_bucket_gapfill('1'::int, time, start=>'1'::int, finish=> '5'::int) bucket, locf(last(data, time)) data_value + FROM (VALUES (1, 1, 1), (1, 4, 4), (2, 1, -1), (2, 4, -4)) b(id, time, data) + WHERE a.id = b.id + GROUP BY b.id, bucket + ) as l on (true); + id | bucket | data_value +----+--------+------------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | 4 | 4 + 2 | 1 | -1 + 2 | 2 | -1 + 2 | 3 | -1 + 2 | 4 | -4 +(8 rows) + +-- test prepared statement +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(value)) +FROM (VALUES (1,1),(2,2)) v(time,value) +GROUP BY 1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +DEALLOCATE prep_gapfill; +-- test column references with TIME_COLUMN last +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test expressions on GROUP BY columns +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + length(color), + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,5; + row_number | locf | color | length | time +------------+------+-------+--------+------ + 1 | | blue | 4 | 0 + 2 | 1 | blue | 4 | 1 + 3 | 1 | blue | 4 | 2 + 4 | 1 | blue | 4 | 3 + 5 | 1 | blue | 4 | 4 + 1 | | red | 3 | 0 + 2 | | red | 3 | 1 + 3 | 2 | red | 3 | 2 + 4 | 2 | red | 3 | 3 + 5 | 2 | red | 3 | 4 +(10 rows) + +-- test columns derived from GROUP BY columns with cast +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | device_id +------+----------- + 0 | 1 + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 1 + 0 | 2 + 1 | 2 + 2 | 2 + 3 | 2 + 4 | 2 +(10 rows) + +-- test columns derived from GROUP BY columns with expression +SELECT + time_bucket_gapfill(1,time,0,5) as time, + 'Device ' || device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | ?column? +------+---------- + 0 | Device 1 + 1 | Device 1 + 2 | Device 1 + 3 | Device 1 + 4 | Device 1 + 0 | Device 2 + 1 | Device 2 + 2 | Device 2 + 3 | Device 2 + 4 | Device 2 +(10 rows) + +--test interpolation with big diifferences in values (test overflows in calculations) +--we use the biggest possible difference in time(x) and the value(y). +--For bigints we also test values of smaller than bigintmax/min to avoid +--the symmetry where x=y (which catches more errors) +SELECT 9223372036854775807 as big_int_max \gset +SELECT -9223372036854775808 as big_int_min \gset +SELECT + time_bucket_gapfill(1,time,0,1) AS time, + interpolate(min(s)) AS "smallint", + interpolate(min(i)) AS "int", + interpolate(min(b)) AS "bigint", + interpolate(min(b2)) AS "bigint2", + interpolate(min(d)) AS "double" +FROM (values (:big_int_min,(-32768)::smallint,(-2147483648)::int,:big_int_min,-2147483648::bigint, '-Infinity'::double precision), + (:big_int_max, 32767::smallint, 2147483647::int,:big_int_max, 2147483647::bigint, 'Infinity'::double precision)) v(time,s,i,b,b2,d) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | bigint2 | double +----------------------+----------+-------------+----------------------+-------------+----------- + -9223372036854775808 | -32768 | -2147483648 | -9223372036854775808 | -2147483648 | -Infinity + 0 | 0 | 0 | 0 | 0 | Infinity + 9223372036854775807 | 32767 | 2147483647 | 9223372036854775807 | 2147483647 | Infinity +(3 rows) + +-- issue #2232: This query used to trigger error "could not find +-- pathkey item to sort" due to a corrupt query plan +SELECT time_bucket_gapfill('1 h', time) AS time, + locf(sum(v1)) AS v1_sum, + interpolate(sum(v2)) AS v2_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v2_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 06:00:00 2018 PST | 2.1 | 65 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 + Mon Jan 01 04:00:00 2018 PST | | +(4 rows) + +-- query without gapfill: +SELECT time_bucket('1 h', time) AS time, + sum(v1) AS v1_sum, + sum(v2) AS v1_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v1_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 +(2 rows) + +-- query to show original data +SELECT * FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +ORDER BY 1 DESC, 2; + time | device_id | v1 | v2 +------------------------------+-----------+-----+---- + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 +(6 rows) + diff --git a/tsl/test/shared/expected/gapfill-13.out b/tsl/test/shared/expected/gapfill-13.out new file mode 100644 index 00000000000..f347927fff4 --- /dev/null +++ b/tsl/test/shared/expected/gapfill-13.out @@ -0,0 +1,3124 @@ +-- 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 EXPLAIN 'EXPLAIN (COSTS OFF)' +-- simple example +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test sorting +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (avg("*VALUES*".column2)) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test sort direction +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 1 DESC; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) DESC + -> Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) NULLS FIRST + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now()) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test order by aggregate function +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1 +ORDER BY 2,1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (avg("*VALUES*".column2)), (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(8 rows) + +-- test query without order by +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,now(),now()), + avg(c2) +FROM (VALUES (now(),1),(now(),NULL),(now(),NULL)) as t(time,c2) +GROUP BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Custom Scan (GapFill) + -> GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, "*VALUES*".column1, now(), now())) + -> Values Scan on "*VALUES*" +(6 rows) + +-- test parallel query +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + avg(value) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- test parallel query with locf +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- test parallel query with interpolate +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(15 rows) + +-- make sure we can run gapfill in parallel workers +-- ensure this plan runs in parallel +:EXPLAIN +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit + -> Sort + Sort Key: (interpolate(avg(gapfill_plan_test.value), NULL::record, NULL::record)) + -> Custom Scan (GapFill) + -> Finalize GroupAggregate + Group Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Gather Merge + Workers Planned: 2 + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> Partial HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_47_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Parallel Append + -> Parallel Seq Scan on _hyper_8_47_chunk + -> Parallel Seq Scan on _hyper_8_48_chunk + -> Parallel Seq Scan on _hyper_8_46_chunk + -> Parallel Seq Scan on _hyper_8_49_chunk +(18 rows) + +-- actually run a parallel gapfill +SELECT + time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), + interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 2 +LIMIT 1; + time_bucket_gapfill | interpolate +------------------------------+------------- + Mon Jan 01 00:00:00 2018 PST | 1 +(1 row) + +-- test sort optimizations +-- test sort optimization with single member order by, +-- should use index scan (no GapFill node for this one since we're not gapfilling) +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(8 rows) + +SET max_parallel_workers_per_gather TO 0; +-- test sort optimizations with locf +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), locf(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(11 rows) + +-- test sort optimizations with interpolate +:EXPLAIN SELECT time_bucket_gapfill('5m',time,to_timestamp(0),to_timestamp(0)), interpolate(avg(value)) +FROM gapfill_plan_test +GROUP BY 1 +ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (GapFill) + -> Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone)) + -> HashAggregate + Group Key: time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone, 'Wed Dec 31 16:00:00 1969 PST'::timestamp with time zone) + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(11 rows) + +RESET max_parallel_workers_per_gather; +CREATE INDEX gapfill_plan_test_indx ON gapfill_plan_test(value, time); +-- test sort optimization with ordering by multiple columns and time_bucket_gapfill not last, +-- must not use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 1,2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)), _hyper_8_46_chunk.value + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(8 rows) + +-- test sort optimization with ordering by multiple columns and time_bucket as last member, +-- should use index scan +:EXPLAIN SELECT time_bucket_gapfill('5m',time),value +FROM gapfill_plan_test +ORDER BY 2,1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: _hyper_8_46_chunk.value, (time_bucket_gapfill('@ 5 mins'::interval, _hyper_8_46_chunk."time", NULL::timestamp with time zone, NULL::timestamp with time zone)) + -> Result + -> Append + -> Seq Scan on _hyper_8_46_chunk + -> Seq Scan on _hyper_8_47_chunk + -> Seq Scan on _hyper_8_48_chunk + -> Seq Scan on _hyper_8_49_chunk +(8 rows) + +\set METRICS metrics_int +-- All test against table :METRICS first +\set ON_ERROR_STOP 0 +-- inverse of previous test query to confirm an error is actually thrown +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time = 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; +ERROR: division by zero +-- test window functions with multiple column references +SELECT + time_bucket_gapfill(1,time,1,2), + first(min(time),min(time)) OVER () +FROM :METRICS +GROUP BY 1; +ERROR: window functions with multiple column references not supported +-- test with unsupported operator +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time =0 AND time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test with 2 tables and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test inner join and where clause doesnt match gapfill argument +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time +WHERE m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- test outer join with constraints in join condition +-- not usable as start/stop +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 LEFT OUTER JOIN :METRICS m2 ON m1.time=m2.time AND m1.time >=0 AND m1.time < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +\set ON_ERROR_STOP 1 +\ir include/gapfill_metrics_query.sql +-- 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. +-- test locf lookup query does not trigger when not needed +-- +-- 1/(SELECT 0) will throw an error in the lookup query but in order to not +-- always trigger evaluation it needs to be correlated otherwise postgres will +-- always run it once even if the value is never used +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | locf3 +------+-----------+-----------+------- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +-- test locf with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value)::int,23) AS locf1, + locf(min(value)::int,(SELECT 42)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and "wrong order" +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + locf(min(value)) AS locf, + locf(min(value),23::float) AS locf1, + locf(min(value),(SELECT 42::float)) AS locf2, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) AS locf3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 1,2,3; + time | device_id | sensor_id | avg | locf | locf1 | locf2 | locf3 +------+-----------+-----------+-----+------+-------+-------+------- + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 0 | 1 | 2 | | | 23 | 42 | -100 + 5 | 1 | 1 | | 5 | 5 | 5 | 5 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 1 | | 5 | 5 | 5 | 5 + 10 | 1 | 2 | | 10 | 10 | 10 | 10 +(6 rows) + +-- test locf with correlated subquery and window functions +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)), + sum(locf(min(value),(SELECT value FROM :METRICS m2 WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1))) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3; + time | device_id | sensor_id | locf | sum +------+-----------+-----------+------+------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 5 | 10 + 10 | 1 | 1 | 5 | 10 + 0 | 1 | 2 | -100 | -100 + 5 | 1 | 2 | 10 | -90 + 10 | 1 | 2 | 10 | 20 +(6 rows) + +-- test JOINs +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id, + d.name, + sensor_id, + s.name, + avg(m.value) +FROM :METRICS m +INNER JOIN devices d USING(device_id) +INNER JOIN sensors s USING(sensor_id) +WHERE time BETWEEN 0 AND 5 +GROUP BY 1,2,3,4,5; + time | device_id | name | sensor_id | name | avg +------+-----------+----------+-----------+----------+----- + 0 | 1 | Device 1 | 1 | Sensor 1 | 5 + 1 | 1 | Device 1 | 1 | Sensor 1 | + 2 | 1 | Device 1 | 1 | Sensor 1 | + 3 | 1 | Device 1 | 1 | Sensor 1 | + 4 | 1 | Device 1 | 1 | Sensor 1 | + 0 | 1 | Device 1 | 2 | Sensor 2 | + 1 | 1 | Device 1 | 2 | Sensor 2 | + 2 | 1 | Device 1 | 2 | Sensor 2 | + 3 | 1 | Device 1 | 2 | Sensor 2 | + 4 | 1 | Device 1 | 2 | Sensor 2 | + 5 | 1 | Device 1 | 2 | Sensor 2 | 10 +(11 rows) + +-- test interpolate with correlated subquery +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + avg(value), + interpolate(min(value)) AS ip, + interpolate(min(value),(-5,-5.0::float),(15,20.0::float)) AS ip1, + interpolate(min(value),(SELECT (-10,-10.0::float)),(SELECT (15,20.0::float))) AS ip2, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) AS ip3 +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | avg | ip | ip1 | ip2 | ip3 +------+-----------+-----------+-----+----+-----+------------------+------------------ + 0 | 1 | 1 | 5 | 5 | 5 | 5 | 5 + 5 | 1 | 1 | | | 10 | 10 | 4.75 + 10 | 1 | 1 | | | 15 | 15 | 4.5 + 0 | 1 | 2 | | | 2.5 | 3.33333333333333 | 4.76190476190476 + 5 | 1 | 2 | 10 | 10 | 10 | 10 | 10 + 10 | 1 | 2 | | | 15 | 15 | 4.21052631578947 +(6 rows) + +-- test interpolate with correlated subquery and window function +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ), + sum(interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + )) OVER (PARTITION BY device_id, sensor_id ROWS 1 PRECEDING) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; + time | device_id | sensor_id | interpolate | sum +------+-----------+-----------+------------------+------------------ + 0 | 1 | 1 | 5 | 5 + 5 | 1 | 1 | 4.75 | 9.75 + 10 | 1 | 1 | 4.5 | 9.25 + 0 | 1 | 2 | 4.76190476190476 | 4.76190476190476 + 5 | 1 | 2 | 10 | 14.7619047619048 + 10 | 1 | 2 | 4.21052631578947 | 14.2105263157895 +(6 rows) + +-- test subqueries +-- subqueries will alter the shape of the plan and top-level constraints +-- might not end up in top-level of jointree +SELECT + time_bucket_gapfill(1,m1.time) +FROM :METRICS m1 +WHERE m1.time >=0 AND m1.time < 2 AND device_id IN (SELECT device_id FROM :METRICS) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test inner join with constraints in join condition +SELECT + time_bucket_gapfill(1,m2.time) +FROM :METRICS m1 INNER JOIN :METRICS m2 ON m1.time=m2.time AND m2.time >=0 AND m2.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test actual table +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS +WHERE time >=0 AND time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test with table alias +SELECT + time_bucket_gapfill(1,time) +FROM :METRICS m +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test with 2 tables +SELECT + time_bucket_gapfill(1,m.time) +FROM :METRICS m, :METRICS m2 +WHERE m.time >=0 AND m.time < 2 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 +(2 rows) + +-- test prepared statement with locf with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + locf(min(value)::int,(SELECT 1/(SELECT 0) FROM :METRICS m2 WHERE m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id ORDER BY time DESC LIMIT 1)) +FROM :METRICS m1 +WHERE time >= 0 AND time < 5 +GROUP BY 1,2,3; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | locf +------+-----------+-----------+------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 5 + 10 | 1 | 1 | 5 +(3 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with interpolate with lookup query +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(5,time,0,11) AS time, + device_id, + sensor_id, + interpolate( + min(value), + (SELECT (time,value) FROM :METRICS m2 + WHERE time<0 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time DESC LIMIT 1), + (SELECT (time,value) FROM :METRICS m2 + WHERE time>10 AND m2.device_id=m1.device_id AND m2.sensor_id=m1.sensor_id + ORDER BY time LIMIT 1) + ) +FROM :METRICS m1 +WHERE time >= 0 AND time < 10 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +EXECUTE prep_gapfill; + time | device_id | sensor_id | interpolate +------+-----------+-----------+------------------ + 0 | 1 | 1 | 5 + 5 | 1 | 1 | 4.75 + 10 | 1 | 1 | 4.5 + 0 | 1 | 2 | 4.76190476190476 + 5 | 1 | 2 | 10 + 10 | 1 | 2 | 4.21052631578947 +(6 rows) + +DEALLOCATE prep_gapfill; +-- test prepared statement with variable gapfill arguments +PREPARE prep_gapfill(int,int,int) AS +SELECT + time_bucket_gapfill($1,time,$2,$3) AS time, + device_id, + sensor_id, + min(value) +FROM :METRICS m1 +WHERE time >= $2 AND time < $3 AND device_id=1 AND sensor_id=1 +GROUP BY 1,2,3 ORDER BY 2,3,1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +EXECUTE prep_gapfill(5,0,10); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 0 | 1 | 1 | 5 + 5 | 1 | 1 | +(2 rows) + +EXECUTE prep_gapfill(4,100,110); + time | device_id | sensor_id | min +------+-----------+-----------+----- + 100 | 1 | 1 | 0 + 104 | 1 | 1 | + 108 | 1 | 1 | +(3 rows) + +DEALLOCATE prep_gapfill; +-- Tests without tables +-- test locf and interpolate call without gapfill +SELECT locf(1); + locf +------ + 1 +(1 row) + +SELECT interpolate(1); + interpolate +------------- + 1 +(1 row) + +-- test locf and interpolate call with NULL input +SELECT locf(NULL::int); + locf +------ + +(1 row) + +SELECT interpolate(NULL); + interpolate +------------- + +(1 row) + +\set ON_ERROR_STOP 0 +-- test time_bucket_gapfill not top level function call +SELECT + 1 + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: no top level time_bucket_gapfill in group by clause +-- test locf with treat_null_as_missing not BOOL +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=1) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function locf(integer, treat_null_as_missing => integer) does not exist at character 46 +-- test locf with treat_null_as_missing not literal +SELECT + time_bucket_gapfill(1,time,1,11), + locf(min(time),treat_null_as_missing:=random()>0) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid locf argument: treat_null_as_missing must be a BOOL literal +-- test interpolate lookup query with 1 element in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT ROW(10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT ROW(10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with 3 elements in record +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: interpolate RECORD arguments must have 2 elements +-- test interpolate lookup query with mismatching time datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10::float,10))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10::float,10))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: first argument of interpolate returned record must match used timestamp datatype +-- test interpolate lookup query with mismatching value datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),next=>(SELECT (10,10::float))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(min(time),prev=>(SELECT (10,10::float))) +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: second argument of interpolate returned record must match used interpolate datatype +-- test interpolate with unsupported datatype +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(text 'text') +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: function interpolate(text) does not exist at character 46 +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interval '1d') +FROM (VALUES (2),(3)) v(time) +GROUP BY 1; +ERROR: function interpolate(interval) does not exist at character 46 +-- test multiple time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time,1,11),time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested time_bucket_gapfill calls +SELECT + time_bucket_gapfill(1,time_bucket_gapfill(1,time,1,11),1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple time_bucket_gapfill calls not allowed +-- test nested locf calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(locf(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test nested interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + interpolate(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test mixed locf/interpolate calls +SELECT + time_bucket_gapfill(1,time,1,11), + locf(interpolate(min(time))) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple interpolate/locf function calls per resultset column not supported +-- test window function inside locf +SELECT + time_bucket_gapfill(1,time,1,11), + locf(avg(min(time)) OVER ()) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window functions must not be below locf +-- test nested window functions +-- prevented by postgres +SELECT + time_bucket_gapfill(1,time,1,11), + avg(avg(min(time)) OVER ()) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: window function calls cannot be nested at character 50 +-- test multiple window functions in single column +SELECT + time_bucket_gapfill(1,time,1,11), + avg(min(time)) OVER () + avg(min(time)) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: multiple window function calls per column not supported +-- test locf not toplevel +SELECT + time_bucket_gapfill(1,time,1,11), + 1 + locf(min(time)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: locf must be toplevel function call +-- test locf inside aggregate +SELECT + time_bucket_gapfill(1,time,1,11), + min(min(locf(time))) OVER () +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: aggregate functions must be below locf +-- test NULL args +SELECT + time_bucket_gapfill(NULL,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width cannot be NULL +SELECT + time_bucket_gapfill(1,NULL,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts cannot be NULL +SELECT + time_bucket_gapfill(1,time,NULL,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +SELECT + time_bucket_gapfill(1,time,1,NULL) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- test 0 bucket_width +SELECT + time_bucket_gapfill(0,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('0d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test negative bucket_width +SELECT + time_bucket_gapfill(-1,time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::date),('2000-02-01'::date)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +SELECT + time_bucket_gapfill('-1d',time,'2000-01-01','2000-02-01') +FROM (VALUES ('2000-01-01'::timestamptz),('2000-02-01'::timestamptz)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be greater than 0 +-- test subqueries as interval, start and stop (not supported atm) +SELECT + time_bucket_gapfill((SELECT 1),time,1,11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +SELECT + time_bucket_gapfill(1,time,(SELECT 1),11) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +SELECT + time_bucket_gapfill(1,time,1,(SELECT 11)) +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +\set ON_ERROR_STOP 1 +-- test time_bucket_gapfill without aggregation +-- this will not trigger gapfilling +SELECT + time_bucket_gapfill(1,time,1,11) +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill +--------------------- + 1 + 2 +(2 rows) + +SELECT + time_bucket_gapfill(1,time,1,11), + avg(time) OVER () +FROM (VALUES (1),(2)) v(time); + time_bucket_gapfill | avg +---------------------+-------------------- + 1 | 1.5000000000000000 + 2 | 1.5000000000000000 +(2 rows) + +-- test int int2/4/8 +SELECT + time_bucket_gapfill(1::int2,time::int2,0::int2,6::int2) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int4,time::int4,0::int4,6::int4) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +SELECT + time_bucket_gapfill(1::int8,time::int8,0::int8,6::int8) +FROM (VALUES (1),(4)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +-- test non-aligned bucket start +SELECT + time_bucket_gapfill(10,time,5,40) +FROM (VALUES (11),(22)) v(time) +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 10 + 20 + 30 +(4 rows) + +-- simple gapfill query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + min(value) AS value +FROM (values (-10,1),(10,2),(11,3),(12,4),(22,5),(30,6),(66,7)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + -10 | 1 + 0 | + 10 | 2 + 20 | 5 + 30 | 6 + 40 | + 60 | 7 +(7 rows) + +-- test references to different columns +SELECT + time_bucket_gapfill(1,t,0,5) as t, + min(t),max(t),min(v),max(v) +FROM(VALUES (1,3),(2,5)) tb(t,v) +GROUP BY 1 ORDER BY 1; + t | min | max | min | max +---+-----+-----+-----+----- + 0 | | | | + 1 | 1 | 1 | 3 | 3 + 2 | 2 | 2 | 5 | 5 + 3 | | | | + 4 | | | | +(5 rows) + +-- test passing of values outside boundaries +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (-1),(1),(3),(6)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + -1 | -1 + 0 | + 1 | 1 + 2 | + 3 | 3 + 4 | + 6 | 6 +(7 rows) + +-- test gap fill before first row and after last row +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | 1 + 2 | 2 + 3 | 3 + 4 | +(5 rows) + +-- test gap fill without rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) +FROM (VALUES (1),(2),(3)) v(time) +WHERE false +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+----- + 0 | + 1 | + 2 | + 3 | + 4 | +(5 rows) + +-- test coalesce +SELECT + time_bucket_gapfill(1,time,0,5), + coalesce(min(time),0), + coalesce(min(value),0), + coalesce(min(value),7) +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | coalesce | coalesce | coalesce +---------------------+----------+----------+---------- + 0 | 0 | 0 | 7 + 1 | 1 | 1 | 1 + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 0 | 0 | 7 +(5 rows) + +-- test case +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + CASE WHEN min(time) IS NOT NULL THEN min(time) ELSE -1 END, + CASE WHEN min(time) IS NOT NULL THEN min(time) + 7 ELSE 0 END, + CASE WHEN 1 = 1 THEN 1 ELSE 0 END +FROM (VALUES (1,1),(2,2),(3,3)) v(time,value) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | case | case | case +---------------------+-----+------+------+------ + 0 | | -1 | 0 | 1 + 1 | 1 | 1 | 8 | 1 + 2 | 2 | 2 | 9 | 1 + 3 | 3 | 3 | 10 | 1 + 4 | | -1 | 0 | 1 +(5 rows) + +-- test constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), min(time), 4 as c +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min | min | c +---------------------+-----+-----+--- + 0 | | | 4 + 1 | 1 | 1 | 4 + 2 | 2 | 2 | 4 + 3 | 3 | 3 | 4 + 4 | | | 4 +(5 rows) + +-- test column reordering +SELECT + 1 as c1, '2' as c2, + time_bucket_gapfill(1,time,0,5), + 3.0 as c3, + min(time), min(time), 4 as c4 +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 3 ORDER BY 3; + c1 | c2 | time_bucket_gapfill | c3 | min | min | c4 +----+----+---------------------+-----+-----+-----+---- + 1 | 2 | 0 | 3.0 | | | 4 + 1 | 2 | 1 | 3.0 | 1 | 1 | 4 + 1 | 2 | 2 | 3.0 | 2 | 2 | 4 + 1 | 2 | 3 | 3.0 | 3 | 3 | 4 + 1 | 2 | 4 | 3.0 | | | 4 +(5 rows) + +-- test timestamptz +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMPTZ '2000-01-01',TIMESTAMPTZ '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMPTZ '2000-01-01 9:00:00'),(TIMESTAMPTZ '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +------------------------------+------------------------------ + Fri Dec 31 22:00:00 1999 PST | + Sat Jan 01 04:00:00 2000 PST | Sat Jan 01 09:00:00 2000 PST + Sat Jan 01 10:00:00 2000 PST | + Sat Jan 01 16:00:00 2000 PST | Sat Jan 01 18:00:00 2000 PST + Sat Jan 01 22:00:00 2000 PST | +(5 rows) + +-- test timestamp +SELECT + time_bucket_gapfill(INTERVAL '6h',time,TIMESTAMP '2000-01-01',TIMESTAMP '2000-01-02'), + min(time) +FROM (VALUES (TIMESTAMP '2000-01-01 9:00:00'),(TIMESTAMP '2000-01-01 18:00:00')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +--------------------------+-------------------------- + Sat Jan 01 00:00:00 2000 | + Sat Jan 01 06:00:00 2000 | Sat Jan 01 09:00:00 2000 + Sat Jan 01 12:00:00 2000 | + Sat Jan 01 18:00:00 2000 | Sat Jan 01 18:00:00 2000 +(4 rows) + +-- test date +SELECT + time_bucket_gapfill(INTERVAL '1w',time,DATE '2000-01-01',DATE '2000-02-10'), + min(time) +FROM (VALUES (DATE '2000-01-08'),(DATE '2000-01-22')) v(time) +GROUP BY 1 ORDER BY 1; + time_bucket_gapfill | min +---------------------+------------ + 12-27-1999 | + 01-03-2000 | 01-08-2000 + 01-10-2000 | + 01-17-2000 | 01-22-2000 + 01-24-2000 | + 01-31-2000 | + 02-07-2000 | +(7 rows) + +-- test grouping by non-time columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test grouping by non-time columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +WHERE false +GROUP BY 1,id ORDER BY 2,1; + time | id | m +------+----+--- +(0 rows) + +-- test duplicate columns in GROUP BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + id, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,2,3 ORDER BY 2,1; + time | id | id | m +------+----+----+--- + 0 | 1 | 1 | + 1 | 1 | 1 | 1 + 2 | 1 | 1 | + 3 | 1 | 1 | + 4 | 1 | 1 | + 0 | 2 | 2 | + 1 | 2 | 2 | + 2 | 2 | 2 | 2 + 3 | 2 | 2 | + 4 | 2 | 2 | +(10 rows) + +-- test grouping by columns not in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + min(value) as m +FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY id,1; + time | m +------+--- + 0 | + 1 | 1 + 2 | + 3 | + 4 | + 0 | + 1 | + 2 | 2 + 3 | + 4 | +(10 rows) + +-- test grouping by non-time columns with text columns +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 1 | blue | 1 + 2 | blue | + 3 | blue | + 4 | blue | + 0 | red | + 1 | red | + 2 | red | 2 + 3 | red | + 4 | red | +(10 rows) + +-- test grouping by non-time columns with text columns with no rows in resultset +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +WHERE false +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- +(0 rows) + +-- test insert into SELECT +SELECT * FROM insert_test; + id +---- + 1 + 2 + 3 + 4 +(4 rows) + +-- test join +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (1,'red',1),(2,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, color, min(value) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | m | m +------+-------+---+--- + 0 | blue | | + 1 | blue | | + 2 | blue | 2 | + 3 | blue | | + 4 | blue | | 2 + 0 | red | | + 1 | red | 1 | + 2 | red | | + 3 | red | | 1 + 4 | red | | +(10 rows) + +-- test join with locf +SELECT t1.*,t2.m FROM +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as locf + FROM + (VALUES (0,'red',1),(0,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t1 INNER JOIN +( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + locf(min(value)) as m + FROM + (VALUES (3,'red',1),(4,'blue',2)) v(time,color,value) + GROUP BY 1,color ORDER BY 2,1 +) t2 ON t1.time = t2.time AND t1.color=t2.color; + time | color | locf | m +------+-------+------+--- + 0 | blue | 2 | + 1 | blue | 2 | + 2 | blue | 2 | + 3 | blue | 2 | + 4 | blue | 2 | 2 + 0 | red | 1 | + 1 | red | 1 | + 2 | red | 1 | + 3 | red | 1 | 1 + 4 | red | 1 | 1 +(10 rows) + +-- test locf +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value)) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=NULL) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | + 40 | + 50 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | + 10 | 9 + 20 | 3 + 30 | 3 + 40 | 3 + 50 | 6 +(6 rows) + +-- test locf with NULLs in first row of resultset and treat_null_as_missing with lookup query +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=false, prev := (SELECT 100)) AS v1, + locf(min(value),treat_null_as_missing:=true, prev := (SELECT 100)) AS v2 +FROM (values (0,NULL),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | v1 | v2 +------+----+----- + 0 | | 100 + 10 | | 100 + 20 | | 100 + 30 | | 100 + 40 | | 100 + 50 | 6 | 6 +(6 rows) + +-- test locf with NULLs in resultset and treat_null_as_missing with resort +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + locf(min(value),treat_null_as_missing:=true) AS value +FROM (values (10,9),(20,3),(30,NULL),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1 DESC; + time | value +------+------- + 50 | 6 + 40 | 3 + 30 | 3 + 20 | 3 + 10 | 9 + 0 | +(6 rows) + +-- test locf with constants +SELECT + time_bucket_gapfill(1,time,0,5), + 2, + locf(min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | ?column? | locf +---------------------+----------+------ + 0 | 2 | 1 + 1 | 2 | 1 + 2 | 2 | 1 + 3 | 2 | 1 + 4 | 2 | 2 +(5 rows) + +-- test expressions inside locf +SELECT + time_bucket_gapfill(1,time,0,5), + locf(min(value)), + locf(4), + locf(4 + min(value)) +FROM (VALUES (0,1,3),(4,2,3)) v(time,value) +GROUP BY 1; + time_bucket_gapfill | locf | locf | locf +---------------------+------+------+------ + 0 | 1 | 4 | 5 + 1 | 1 | 4 | 5 + 2 | 1 | 4 | 5 + 3 | 1 | 4 | 5 + 4 | 2 | 4 | 6 +(5 rows) + +-- test locf with out of boundary lookup +SELECT + time_bucket_gapfill(10,time,0,70) AS time, + locf(min(value),(SELECT 100)) AS value +FROM (values (20,9),(40,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 100 + 10 | 100 + 20 | 9 + 30 | 9 + 40 | 6 + 50 | 6 + 60 | 6 +(7 rows) + +-- test locf with different datatypes +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1)) AS text, + locf(min(v2)) AS "int[]", + locf(min(v3)) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test locf with different datatypes and treat_null_as_missing +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(v1),treat_null_as_missing:=true) AS text, + locf(min(v2),treat_null_as_missing:=true) AS "int[]", + locf(min(v3),treat_null_as_missing:=true) AS "text 4/8k" +FROM (VALUES + (1,'foo',ARRAY[1,2,3],repeat('4k',2048)), + (2,NULL,NULL,NULL), + (3,'bar',ARRAY[3,4,5],repeat('8k',4096)) +) v(time,v1,v2,v3) +GROUP BY 1; + time | text | int[] | text 4/8k +------+------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | | | + 1 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 2 | foo | {1,2,3} | 4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k4k + 3 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k + 4 | bar | {3,4,5} | 8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k8k +(5 rows) + +-- test interpolate +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(value)) AS value +FROM (values (0,1),(50,6)) v(time,value) +GROUP BY 1 ORDER BY 1; + time | value +------+------- + 0 | 1 + 10 | 2 + 20 | 3 + 30 | 4 + 40 | 5 + 50 | 6 +(6 rows) + +-- test interpolate with NULL values +SELECT + time_bucket_gapfill(1,time,0,5) AS time, + interpolate(avg(temp)) AS temp +FROM (VALUES (0,0),(2,NULL),(5,5)) v(time,temp) +GROUP BY 1; + time | temp +------+------ + 0 | 0 + 1 | + 2 | + 3 | + 4 | + 5 | 5 +(6 rows) + +-- test interpolate datatypes +SELECT + time_bucket_gapfill(10,time,0,50) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (0,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(50,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + 0 | -3 | -3 | -3 | -3 | -3 + 10 | -2 | -2 | -2 | -1.8 | -1.8 + 20 | -1 | -1 | -1 | -0.6 | -0.6 + 30 | 1 | 1 | 1 | 0.6 | 0.6 + 40 | 2 | 2 | 2 | 1.8 | 1.8 + 50 | 3 | 3 | 3 | 3 | 3 +(6 rows) + +-- test interpolate datatypes with negative time +SELECT + time_bucket_gapfill(10,time,-40,30) AS time, + interpolate(min(v1)) AS "smallint", + interpolate(min(v2)) AS "int", + interpolate(min(v3)) AS "bigint", + interpolate(min(v4)) AS "float4", + interpolate(min(v5)) AS "float8" +FROM (values (-40,-3::smallint,-3::int,-3::bigint,-3::float4,-3::float8),(20,3::smallint,3::int,3::bigint,3::float4,3::float8)) v(time,v1,v2,v3,v4,v5) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | float4 | float8 +------+----------+-----+--------+--------+-------- + -40 | -3 | -3 | -3 | -3 | -3 + -30 | -2 | -2 | -2 | -2 | -2 + -20 | -1 | -1 | -1 | -1 | -1 + -10 | 0 | 0 | 0 | 0 | 0 + 0 | 1 | 1 | 1 | 1 | 1 + 10 | 2 | 2 | 2 | 2 | 2 + 20 | 3 | 3 | 3 | 3 | 3 +(7 rows) + +-- test interpolate with multiple groupings +SELECT + time_bucket_gapfill(5,time,0,11), + device, + interpolate(min(v1),(SELECT (-10,-10)),(SELECT (20,10))) +FROM (VALUES (5,1,0),(5,2,0)) as v(time,device,v1) +GROUP BY 1,2 ORDER BY 2,1; + time_bucket_gapfill | device | interpolate +---------------------+--------+------------- + 0 | 1 | -3 + 5 | 1 | 0 + 10 | 1 | 3 + 0 | 2 | -3 + 5 | 2 | 0 + 10 | 2 | 3 +(6 rows) + +-- test cte with gap filling in outer query +WITH data AS ( + SELECT * FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) +) +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM data +GROUP BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test cte with gap filling in inner query +WITH gapfill AS ( + SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m + FROM (VALUES (1,1,1),(2,2,2)) v(time,id,value) + GROUP BY 1,id +) +SELECT * FROM gapfill; + time | id | m +------+----+--- + 0 | 1 | + 1 | 1 | 1 + 2 | 1 | + 3 | 1 | + 4 | 1 | + 0 | 2 | + 1 | 2 | + 2 | 2 | 2 + 3 | 2 | + 4 | 2 | +(10 rows) + +-- test window functions +SELECT + time_bucket_gapfill(10,time,0,60), + interpolate(min(time)), + lag(min(time)) OVER () +FROM (VALUES (0),(50)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | lag +---------------------+-------------+----- + 0 | 0 | + 10 | 10 | 0 + 20 | 20 | + 30 | 30 | + 40 | 40 | + 50 | 50 | +(6 rows) + +-- test window functions with multiple windows +SELECT + time_bucket_gapfill(1,time,0,10), + interpolate(min(time)), + row_number() OVER (), + locf(min(time)), + sum(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 2 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 3 PRECEDING), + sum(interpolate(min(time))) OVER (ROWS 4 PRECEDING) +FROM (VALUES (0),(9)) v(time) +GROUP BY 1; + time_bucket_gapfill | interpolate | row_number | locf | sum | sum | sum | sum +---------------------+-------------+------------+------+-----+-----+-----+----- + 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + 1 | 1 | 2 | 0 | 1 | 1 | 1 | 1 + 2 | 2 | 3 | 0 | 3 | 3 | 3 | 3 + 3 | 3 | 4 | 0 | 5 | 6 | 6 | 6 + 4 | 4 | 5 | 0 | 7 | 9 | 10 | 10 + 5 | 5 | 6 | 0 | 9 | 12 | 14 | 15 + 6 | 6 | 7 | 0 | 11 | 15 | 18 | 20 + 7 | 7 | 8 | 0 | 13 | 18 | 22 | 25 + 8 | 8 | 9 | 0 | 15 | 21 | 26 | 30 + 9 | 9 | 10 | 9 | 17 | 24 | 30 | 35 +(10 rows) + +-- test window functions with constants +SELECT + time_bucket_gapfill(1,time,0,5), + min(time), + 4 as c, + lag(min(time)) OVER () +FROM (VALUES (1),(2),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | c | lag +---------------------+-----+---+----- + 0 | | 4 | + 1 | 1 | 4 | + 2 | 2 | 4 | 1 + 3 | 3 | 4 | 2 + 4 | | 4 | 3 +(5 rows) + +--test window functions with locf +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + locf(min(time)) AS locf, + lag(locf(min(time))) over () AS lag_locf, + lead(locf(min(time))) over () AS lead_locf +FROM (VALUES (1),(2)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | locf | lag_locf | lead_locf +---------------------+-----+---------+----------+------+----------+----------- + 0 | | | 1 | | | 1 + 1 | 1 | | 2 | 1 | | 2 + 2 | 2 | 1 | | 2 | 1 | 2 + 3 | | 2 | | 2 | 2 | 2 + 4 | | | | 2 | 2 | +(5 rows) + +--test window functions with interpolate +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + lead(min(time)) over () AS lead_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + lead(interpolate(min(time))) over () AS lead_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lead_min | interpolate | lag_interpolate | lead_interpolate +---------------------+-----+---------+----------+-------------+-----------------+------------------ + 0 | | | 1 | | | 1 + 1 | 1 | | | 1 | | 2 + 2 | | 1 | 3 | 2 | 1 | 3 + 3 | 3 | | | 3 | 2 | + 4 | | 3 | | | 3 | +(5 rows) + +--test window functions with expressions +SELECT + time_bucket_gapfill(1,time,0,5), + min(time) AS "min", + lag(min(time)) over () AS lag_min, + 1 + lag(min(time)) over () AS lag_min, + interpolate(min(time)) AS interpolate, + lag(interpolate(min(time))) over () AS lag_interpolate, + 1 + lag(interpolate(min(time))) over () AS lag_interpolate +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | min | lag_min | lag_min | interpolate | lag_interpolate | lag_interpolate +---------------------+-----+---------+---------+-------------+-----------------+----------------- + 0 | | | | | | + 1 | 1 | | | 1 | | + 2 | | 1 | 2 | 2 | 1 | 2 + 3 | 3 | | | 3 | 2 | 3 + 4 | | 3 | 4 | | 3 | 4 +(5 rows) + +--test row_number/rank/percent_rank/... window functions with gapfill reference +SELECT + time_bucket_gapfill(1,time,0,5), + ntile(2) OVER () AS ntile_2, + ntile(3) OVER () AS ntile_3, + ntile(5) OVER () AS ntile_5, + row_number() OVER (), + cume_dist() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + rank() OVER (), + rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)), + percent_rank() OVER (ORDER BY time_bucket_gapfill(1,time,0,5)) +FROM (VALUES (1),(3)) v(time) +GROUP BY 1; + time_bucket_gapfill | ntile_2 | ntile_3 | ntile_5 | row_number | cume_dist | rank | rank | percent_rank +---------------------+---------+---------+---------+------------+-----------+------+------+-------------- + 0 | 1 | 1 | 1 | 1 | 0.2 | 1 | 1 | 0 + 1 | 1 | 1 | 2 | 2 | 0.4 | 1 | 2 | 0.25 + 2 | 1 | 2 | 3 | 3 | 0.6 | 1 | 3 | 0.5 + 3 | 2 | 2 | 4 | 4 | 0.8 | 1 | 4 | 0.75 + 4 | 2 | 3 | 5 | 5 | 1 | 1 | 5 | 1 +(5 rows) + +-- test first_value/last_value/nth_value +SELECT + time_bucket_gapfill(1,time,0,5), + first_value(min(time)) OVER (), + nth_value(min(time),3) OVER (), + last_value(min(time)) OVER () +FROM (VALUES (0),(2),(5)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | nth_value | last_value +---------------------+-------------+-----------+------------ + 0 | 0 | 2 | 5 + 1 | 0 | 2 | 5 + 2 | 0 | 2 | 5 + 3 | 0 | 2 | 5 + 4 | 0 | 2 | 5 + 5 | 0 | 2 | 5 +(6 rows) + +-- test window functions with PARTITION BY +SELECT + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (), + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | row_number | row_number +------+-------+------------+------------ + 0 | blue | 1 | 1 + 1 | blue | 2 | 2 + 2 | blue | 3 | 3 + 3 | blue | 4 | 4 + 4 | blue | 5 | 5 + 0 | red | 6 | 1 + 1 | red | 7 | 2 + 2 | red | 8 | 3 + 3 | red | 9 | 4 + 4 | red | 10 | 5 +(10 rows) + +-- test multiple windows +\set ON_ERROR_STOP 0 +SELECT + time_bucket_gapfill(1,time,0,11), + first_value(interpolate(min(time))) OVER (ROWS 1 PRECEDING), + interpolate(min(time)), + last_value(interpolate(min(time))) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +FROM (VALUES (0),(10)) v(time) +GROUP BY 1; + time_bucket_gapfill | first_value | interpolate | last_value +---------------------+-------------+-------------+------------ + 0 | 0 | 0 | 1 + 1 | 0 | 1 | 2 + 2 | 1 | 2 | 3 + 3 | 2 | 3 | 4 + 4 | 3 | 4 | 5 + 5 | 4 | 5 | 6 + 6 | 5 | 6 | 7 + 7 | 6 | 7 | 8 + 8 | 7 | 8 | 9 + 9 | 8 | 9 | 10 + 10 | 9 | 10 | 10 +(11 rows) + +-- test reorder +SELECT + time_bucket_gapfill(1,time,0,5) as time, + id, + min(value) as m +FROM + (VALUES (1,1,1),(2,2,2)) v(time,id,value) +GROUP BY 1,id ORDER BY 1,id; + time | id | m +------+----+--- + 0 | 1 | + 0 | 2 | + 1 | 1 | 1 + 1 | 2 | + 2 | 1 | + 2 | 2 | 2 + 3 | 1 | + 3 | 2 | + 4 | 1 | + 4 | 2 | +(10 rows) + +-- test order by locf +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | locf +---------------------+------ + 1 | + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + locf(min(time)) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | locf +---------------------+------ + 2 | 2 + 3 | 3 + 4 | 3 + 5 | 3 + 1 | +(5 rows) + +-- test order by interpolate +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 1,2; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS FIRST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 4 | + 5 | + 1 | 1 + 2 | 2 + 3 | 3 +(5 rows) + +SELECT + time_bucket_gapfill(1,time,1,6), + interpolate(min(time),prev:=(0,0)::record) +FROM + (VALUES (2),(3)) v(time) +GROUP BY 1 ORDER BY 2 NULLS LAST,1; + time_bucket_gapfill | interpolate +---------------------+------------- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | + 5 | +(5 rows) + +-- test queries on hypertable +-- test locf and interpolate together +SELECT + time_bucket_gapfill(interval '1h',time,timestamptz '2018-01-01 05:00:00-8', timestamptz '2018-01-01 07:00:00-8'), + device_id, + locf(avg(v1)) AS locf_v1, + locf(min(v2)) AS locf_v2, + interpolate(avg(v1)) AS interpolate_v1, + interpolate(avg(v2)) AS interpolate_v2 +FROM metrics_tstz +GROUP BY 1,2 +ORDER BY 1,2; + time_bucket_gapfill | device_id | locf_v1 | locf_v2 | interpolate_v1 | interpolate_v2 +------------------------------+-----------+---------+---------+----------------+---------------- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.5 | 10 | 0.25 | 5 + Mon Jan 01 06:00:00 2018 PST | 2 | 0.7 | 20 | 1.05 | 30 + Mon Jan 01 06:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 | 0.9 | 30 +(9 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +SELECT + time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz), + interpolate( + avg(v1), + (SELECT ('2017-01-01'::timestamptz,1::float)), + (SELECT ('2017-01-02'::timestamptz,2::float)) + ) +FROM metrics_tstz WHERE time_bucket_gapfill('12h'::interval,time,'2017-01-01'::timestamptz, '2017-01-02'::timestamptz) < '2017-01-01' GROUP BY 1; + time_bucket_gapfill | interpolate +------------------------------+------------------- + Sat Dec 31 16:00:00 2016 PST | 0.666666666666667 + Sun Jan 01 04:00:00 2017 PST | 1.16666666666667 + Sun Jan 01 16:00:00 2017 PST | 1.66666666666667 +(3 rows) + +-- interpolation with correlated subquery lookup before interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 3:00 PST'::timestamptz, '2018-01-01 8:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + (SELECT (time,0.5::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time DESC LIMIT 1) + ), + avg(v1) +FROM metrics_tstz m1 +WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 03:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 04:00:00 2018 PST | 1 | 0.5 | + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 +(5 rows) + +-- interpolation with correlated subquery lookup after interval +SELECT + time_bucket_gapfill('1h'::interval,time,'2018-01-01 5:00 PST'::timestamptz, '2018-01-01 9:00 PST'::timestamptz), + device_id, + interpolate( + avg(v1), + next=>(SELECT (time,v2::float) FROM metrics_tstz m2 WHERE m1.device_id=m2.device_id ORDER BY time LIMIT 1) + ),avg(v1) +FROM metrics_tstz m1 WHERE device_id=1 GROUP BY 1,2 ORDER BY 1,2; + time_bucket_gapfill | device_id | interpolate | avg +------------------------------+-----------+-------------+----- + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 0.5 + Mon Jan 01 06:00:00 2018 PST | 1 | 0.25 | + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 08:00:00 2018 PST | 1 | -5 | +(4 rows) + +\set ON_ERROR_STOP 0 +-- bucket_width non simple expression +SELECT + time_bucket_gapfill(t,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: bucket_width must be a simple expression +-- no start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL start/finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- no start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,finish:=1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- unsupported start expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start must be a simple expression +-- NULL start and no usable time constraints +SELECT + time_bucket_gapfill(1,t,NULL,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- NULL finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- unsupported finish expression and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,t) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish must be a simple expression +-- no finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- NULL finish and no usable time constraints +SELECT + time_bucket_gapfill(1,t,1,NULL) +FROM (VALUES (1),(2)) v(t) +WHERE true AND true +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer finish from WHERE clause +-- expression with column reference on right side +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE t > t AND t < 2 +GROUP BY 1; +ERROR: missing time_bucket_gapfill argument: could not infer start from WHERE clause +-- expression with cast +SELECT + time_bucket_gapfill(1,t1::int8) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 >= 1 AND t1 <= 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +-- expression with multiple column references +SELECT + time_bucket_gapfill(1,t1+t2) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 1 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +-- expression with NULL start in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END AND t1 < 4 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- expression with NULL finish in WHERE clause, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < CASE WHEN length(version()) > 0 THEN NULL::int ELSE NULL::int END +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- non-Const NULL as start argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: start cannot be NULL +-- non-Const NULL as finish argument, we use CASE to wrap the NULL so it doesnt get folded +SELECT + time_bucket_gapfill(1,t1,NULL,CASE WHEN length(version())>0 THEN NULL::int ELSE NULL::int END) +FROM (VALUES (1,2),(2,2)) v(t1,t2) +WHERE t1 > 0 AND t1 < 2 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: finish cannot be NULL +-- time_bucket_gapfill with constraints ORed +SELECT + time_bucket_gapfill(1::int8,t::int8) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 OR t < 3 +GROUP BY 1; +ERROR: invalid time_bucket_gapfill argument: ts needs to refer to a single column if no start or finish is supplied +\set ON_ERROR_STOP 1 +-- int32 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- same query with less or equal as finish +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t <= 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 + 3 +(5 rows) + +-- int32 time_bucket_gapfill with start column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + -1 < t AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 +(3 rows) + +-- int32 time_bucket_gapfill with finish column and value switched +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= 0 AND 3 >= t +GROUP BY 1; + time_bucket_gapfill +--------------------- + 0 + 1 + 2 + 3 +(4 rows) + +-- int16 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int2,t) +FROM (VALUES (1::int2),(2::int2)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- int64 time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill(1::int8,t) +FROM (VALUES (1::int8),(2::int8)) v(t) +WHERE + t >= -1 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- date time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('1d'::interval,t) +FROM (VALUES ('1999-12-30'::date),('2000-01-01'::date)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +--------------------- + 12-29-1999 + 12-30-1999 + 12-31-1999 + 01-01-2000 + 01-02-2000 +(5 rows) + +-- timestamp time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamp),('2000-01-01'::timestamp)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +-------------------------- + Wed Dec 29 00:00:00 1999 + Wed Dec 29 12:00:00 1999 + Thu Dec 30 00:00:00 1999 + Thu Dec 30 12:00:00 1999 + Fri Dec 31 00:00:00 1999 + Fri Dec 31 12:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sat Jan 01 12:00:00 2000 + Sun Jan 02 00:00:00 2000 + Sun Jan 02 12:00:00 2000 +(10 rows) + +-- timestamptz time_bucket_gapfill with no start/finish +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '1999-12-29' AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Tue Dec 28 16:00:00 1999 PST + Wed Dec 29 04:00:00 1999 PST + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(11 rows) + +-- timestamptz time_bucket_gapfill with more complex expression +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03' +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- timestamptz time_bucket_gapfill with different datatype in finish constraint +SELECT + time_bucket_gapfill('12h'::interval,t) +FROM (VALUES ('1999-12-30'::timestamptz),('2000-01-01'::timestamptz)) v(t) +WHERE + t >= '2000-01-03'::timestamptz - '4d'::interval AND t < '2000-01-03'::date +GROUP BY 1; + time_bucket_gapfill +------------------------------ + Wed Dec 29 16:00:00 1999 PST + Thu Dec 30 04:00:00 1999 PST + Thu Dec 30 16:00:00 1999 PST + Fri Dec 31 04:00:00 1999 PST + Fri Dec 31 16:00:00 1999 PST + Sat Jan 01 04:00:00 2000 PST + Sat Jan 01 16:00:00 2000 PST + Sun Jan 02 04:00:00 2000 PST + Sun Jan 02 16:00:00 2000 PST +(9 rows) + +-- time_bucket_gapfill with now() as start +SELECT + time_bucket_gapfill('1h'::interval,t) +FROM (VALUES (now()),(now())) v(t) +WHERE + t >= now() AND t < now() - '1h'::interval +GROUP BY 1; + time_bucket_gapfill +--------------------- +(0 rows) + +-- time_bucket_gapfill with multiple constraints +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t >= -1 AND t < 3 and t>1 AND t <=4 AND length(version()) > 0 +GROUP BY 1; + time_bucket_gapfill +--------------------- + 2 +(1 row) + +-- int32 time_bucket_gapfill with greater for start +SELECT + time_bucket_gapfill(1,t) +FROM (VALUES (1),(2)) v(t) +WHERE + t > -2 AND t < 3 +GROUP BY 1; + time_bucket_gapfill +--------------------- + -1 + 0 + 1 + 2 +(4 rows) + +-- test DISTINCT +SELECT DISTINCT ON (color) + time_bucket_gapfill(1,time,0,5) as time, + color, + min(value) as m +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color ORDER BY 2,1; + time | color | m +------+-------+--- + 0 | blue | + 0 | red | +(2 rows) + +-- test DISTINCT with window functions +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER () +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 6 + 1 | red | 7 + 2 | red | 8 + 3 | red | 9 + 4 | red | 10 +(10 rows) + +-- test DISTINCT with window functions and PARTITION BY +SELECT DISTINCT ON (color,row_number() OVER (PARTITION BY color)) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test DISTINCT with window functions not in targetlist +SELECT DISTINCT ON (row_number() OVER ()) + time_bucket_gapfill(1,time,0,5) as time, + color, + row_number() OVER (PARTITION BY color) +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 1,color; + time | color | row_number +------+-------+------------ + 0 | blue | 1 + 1 | blue | 2 + 2 | blue | 3 + 3 | blue | 4 + 4 | blue | 5 + 0 | red | 1 + 1 | red | 2 + 2 | red | 3 + 3 | red | 4 + 4 | red | 5 +(10 rows) + +-- test column references +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test with Nested Loop +SELECT l.id, bucket, data_value FROM + (VALUES (1), (2), (3), (4)) a(id) + INNER JOIN LATERAL ( + SELECT b.id id, time_bucket_gapfill('1'::int, time, start=>'1'::int, finish=> '5'::int) bucket, locf(last(data, time)) data_value + FROM (VALUES (1, 1, 1), (1, 4, 4), (2, 1, -1), (2, 4, -4)) b(id, time, data) + WHERE a.id = b.id + GROUP BY b.id, bucket + ) as l on (true); + id | bucket | data_value +----+--------+------------ + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 1 | 4 | 4 + 2 | 1 | -1 + 2 | 2 | -1 + 2 | 3 | -1 + 2 | 4 | -4 +(8 rows) + +-- test prepared statement +PREPARE prep_gapfill AS +SELECT + time_bucket_gapfill(1,time,0,5) as time, + locf(min(value)) +FROM (VALUES (1,1),(2,2)) v(time,value) +GROUP BY 1; +-- execute 10 times to make sure turning it into generic plan works +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +EXECUTE prep_gapfill; + time | locf +------+------ + 0 | + 1 | 1 + 2 | 2 + 3 | 2 + 4 | 2 +(5 rows) + +DEALLOCATE prep_gapfill; +-- test column references with TIME_COLUMN last +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,4; + row_number | locf | color | time +------------+------+-------+------ + 1 | | blue | 0 + 2 | 1 | blue | 1 + 3 | 1 | blue | 2 + 4 | 1 | blue | 3 + 5 | 1 | blue | 4 + 1 | | red | 0 + 2 | | red | 1 + 3 | 2 | red | 2 + 4 | 2 | red | 3 + 5 | 2 | red | 4 +(10 rows) + +-- test expressions on GROUP BY columns +SELECT + row_number() OVER (PARTITION BY color), + locf(min(time)), + color, + length(color), + time_bucket_gapfill(1,time,0,5) as time +FROM (VALUES (1,'blue',1),(2,'red',2)) v(time,color,value) +GROUP BY 3,5; + row_number | locf | color | length | time +------------+------+-------+--------+------ + 1 | | blue | 4 | 0 + 2 | 1 | blue | 4 | 1 + 3 | 1 | blue | 4 | 2 + 4 | 1 | blue | 4 | 3 + 5 | 1 | blue | 4 | 4 + 1 | | red | 3 | 0 + 2 | | red | 3 | 1 + 3 | 2 | red | 3 | 2 + 4 | 2 | red | 3 | 3 + 5 | 2 | red | 3 | 4 +(10 rows) + +-- test columns derived from GROUP BY columns with cast +SELECT + time_bucket_gapfill(1,time,0,5) as time, + device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | device_id +------+----------- + 0 | 1 + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 1 + 0 | 2 + 1 | 2 + 2 | 2 + 3 | 2 + 4 | 2 +(10 rows) + +-- test columns derived from GROUP BY columns with expression +SELECT + time_bucket_gapfill(1,time,0,5) as time, + 'Device ' || device_id::text +FROM (VALUES (1,1),(2,2)) v(time,device_id) +GROUP BY 1,device_id; + time | ?column? +------+---------- + 0 | Device 1 + 1 | Device 1 + 2 | Device 1 + 3 | Device 1 + 4 | Device 1 + 0 | Device 2 + 1 | Device 2 + 2 | Device 2 + 3 | Device 2 + 4 | Device 2 +(10 rows) + +--test interpolation with big diifferences in values (test overflows in calculations) +--we use the biggest possible difference in time(x) and the value(y). +--For bigints we also test values of smaller than bigintmax/min to avoid +--the symmetry where x=y (which catches more errors) +SELECT 9223372036854775807 as big_int_max \gset +SELECT -9223372036854775808 as big_int_min \gset +SELECT + time_bucket_gapfill(1,time,0,1) AS time, + interpolate(min(s)) AS "smallint", + interpolate(min(i)) AS "int", + interpolate(min(b)) AS "bigint", + interpolate(min(b2)) AS "bigint2", + interpolate(min(d)) AS "double" +FROM (values (:big_int_min,(-32768)::smallint,(-2147483648)::int,:big_int_min,-2147483648::bigint, '-Infinity'::double precision), + (:big_int_max, 32767::smallint, 2147483647::int,:big_int_max, 2147483647::bigint, 'Infinity'::double precision)) v(time,s,i,b,b2,d) +GROUP BY 1 ORDER BY 1; + time | smallint | int | bigint | bigint2 | double +----------------------+----------+-------------+----------------------+-------------+----------- + -9223372036854775808 | -32768 | -2147483648 | -9223372036854775808 | -2147483648 | -Infinity + 0 | 0 | 0 | 0 | 0 | Infinity + 9223372036854775807 | 32767 | 2147483647 | 9223372036854775807 | 2147483647 | Infinity +(3 rows) + +-- issue #2232: This query used to trigger error "could not find +-- pathkey item to sort" due to a corrupt query plan +SELECT time_bucket_gapfill('1 h', time) AS time, + locf(sum(v1)) AS v1_sum, + interpolate(sum(v2)) AS v2_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v2_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 06:00:00 2018 PST | 2.1 | 65 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 + Mon Jan 01 04:00:00 2018 PST | | +(4 rows) + +-- query without gapfill: +SELECT time_bucket('1 h', time) AS time, + sum(v1) AS v1_sum, + sum(v2) AS v1_sum +FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +GROUP BY 1 +ORDER BY 1 DESC; + time | v1_sum | v1_sum +------------------------------+--------+-------- + Mon Jan 01 07:00:00 2018 PST | 2.3 | 70 + Mon Jan 01 05:00:00 2018 PST | 2.1 | 60 +(2 rows) + +-- query to show original data +SELECT * FROM metrics_tstz +WHERE time >= '2018-01-01 04:00' AND time < '2018-01-01 08:00' +ORDER BY 1 DESC, 2; + time | device_id | v1 | v2 +------------------------------+-----------+-----+---- + Mon Jan 01 07:00:00 2018 PST | 1 | 0 | 0 + Mon Jan 01 07:00:00 2018 PST | 2 | 1.4 | 40 + Mon Jan 01 07:00:00 2018 PST | 3 | 0.9 | 30 + Mon Jan 01 05:00:00 2018 PST | 1 | 0.5 | 10 + Mon Jan 01 05:00:00 2018 PST | 2 | 0.7 | 20 + Mon Jan 01 05:00:00 2018 PST | 3 | 0.9 | 30 +(6 rows) + diff --git a/tsl/test/shared/expected/ordered_append-13.out b/tsl/test/shared/expected/ordered_append-13.out new file mode 100644 index 00000000000..57be094aed1 --- /dev/null +++ b/tsl/test/shared/expected/ordered_append-13.out @@ -0,0 +1,4459 @@ +-- 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('include/%s.sql', :'TEST_BASE_NAME') as "TEST_QUERY_NAME", + format('%s/shared/results/%s_results_uncompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_UNCOMPRESSED", + format('%s/shared/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESSED" +\gset +SELECT format('\! diff -u --label "Uncompressed results" --label "Compressed results" %s %s', :'TEST_RESULTS_UNCOMPRESSED', :'TEST_RESULTS_COMPRESSED') as "DIFF_CMD" +\gset +-- get EXPLAIN output for all variations +\set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)' +\set PREFIX_VERBOSE 'EXPLAIN (analyze, costs off, timing off, summary off, verbose)' +set work_mem to '64MB'; +set max_parallel_workers_per_gather to 0; +\set TEST_TABLE 'metrics' +\ir :TEST_QUERY_NAME +-- 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. +-- test ASC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test DESC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test query with ORDER BY column not in targetlist +:PREFIX +SELECT pg_typeof(device_id), + pg_typeof(v2) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) +(6 rows) + +-- ORDER BY may include other columns after time column +:PREFIX +SELECT time, + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) +(9 rows) + +-- test RECORD in targetlist +:PREFIX +SELECT (time, + device_id, + v0) +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) +(9 rows) + +-- test sort column not in targetlist +:PREFIX +SELECT time_bucket('1h', time) +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- queries with ORDER BY non-time column shouldn't use ordered append +:PREFIX +SELECT device_id +FROM :TEST_TABLE +ORDER BY device_id +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_1_1_chunk.device_id + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(9 rows) + +-- time column must be primary sort order +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY device_id, + time +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Incremental Sort (actual rows=1 loops=1) + Sort Key: _hyper_1_1_chunk.device_id, _hyper_1_1_chunk."time" + Presorted Key: _hyper_1_1_chunk.device_id + Full-sort Groups: 1 Sort Method: top-N heapsort Average Peak Memory: 25kB + Pre-sorted Groups: 1 Sort Method: top-N heapsort Average Peak Memory: 25kB + -> Merge Append (actual rows=13675 loops=1) + Sort Key: _hyper_1_1_chunk.device_id + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=3599 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 3599 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=5039 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 5039 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=5039 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 5039 +(17 rows) + +-- test equality constraint on ORDER BY prefix +-- currently not optimized +SET enable_seqscan TO false; +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY device_id, + time +LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Append (actual rows=10 loops=1) + Sort Key: _hyper_1_1_chunk."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=10 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 10 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 +(12 rows) + +RESET enable_seqscan; +-- queries without LIMIT should use ordered append +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time ASC; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics (actual rows=27348 loops=1) + Order: metrics."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=7196 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 10794 + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=10076 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 15114 + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=10076 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 15114 +(11 rows) + +-- queries without ORDER BY shouldnt use ordered append +:PREFIX +SELECT pg_typeof(time) +FROM :TEST_TABLE +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------ + 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) +(6 rows) + +-- test interaction with constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 +(9 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 +(9 rows) + +-- test interaction with runtime exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + Chunks excluded during startup: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 +(10 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + Chunks excluded during startup: 1 + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 +(10 rows) + +-- test constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz + AND time < '2000-01-10' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + Chunks excluded during startup: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 +(7 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz + AND time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + Chunks excluded during startup: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 +(7 rows) + +-- min/max queries +:PREFIX +SELECT max(time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(14 rows) + +:PREFIX +SELECT min(time) +FROM :TEST_TABLE; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(14 rows) + +-- test first/last (doesn't use ordered append yet) +:PREFIX +SELECT first(time, time) +FROM :TEST_TABLE; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(14 rows) + +:PREFIX +SELECT last(time, time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(14 rows) + +-- test query with time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test query with ORDER BY time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: time_bucket('@ 1 day'::interval, metrics."time") + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test query with ORDER BY time_bucket, device_id +-- must not use ordered append +:PREFIX +SELECT time_bucket('1d', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time_bucket('1d', time), + device_id +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_1_1_chunk."time")), _hyper_1_1_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Index Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=7196 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=10076 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=10076 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) +(12 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY date_trunc('day', time) +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: date_trunc('day'::text, metrics."time") + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT date_trunc('day', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: date_trunc('day'::text, metrics."time") + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test query with ORDER BY date_trunc, device_id +-- must not use ordered append +:PREFIX +SELECT date_trunc('day', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY 1, + 2 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_1_1_chunk."time")), _hyper_1_1_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Index Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=7196 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=10076 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=10076 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) +(12 rows) + +-- test query with now() should result in ordered ChunkAppend +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < now() + '1 month' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + Chunks excluded during startup: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 +(13 rows) + +-- test CTE +:PREFIX WITH i AS ( + SELECT time + FROM :TEST_TABLE + WHERE time < now() + ORDER BY time DESC + LIMIT 100 +) +SELECT * +FROM i; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=100 loops=1) + Order: metrics."time" DESC + Chunks excluded during startup: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=100 loops=1) + Index Cond: ("time" < now()) + Heap Fetches: 100 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 +(13 rows) + +-- test CTE +-- no chunk exclusion for CTE because cte query is not pulled up +:PREFIX WITH cte AS ( + SELECT time + FROM :TEST_TABLE + WHERE device_id = 1 + ORDER BY time +) +SELECT * +FROM cte +WHERE time < '2000-02-01'::timestamptz; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics (actual rows=13674 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (actual rows=3598 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 +(11 rows) + +-- test subquery +-- not ChunkAppend so no chunk exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time = ( + SELECT max(time) + FROM :TEST_TABLE) +ORDER BY time; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics (actual rows=5 loops=1) + Chunks excluded during runtime: 2 + InitPlan 2 (returns $1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics metrics_1 (actual rows=1 loops=1) + Order: metrics_1."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=5 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 5 +(26 rows) + +-- test ordered append with limit expression +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT ( + SELECT length('four')); + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=4 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=4 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=4 loops=1) + Heap Fetches: 4 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(11 rows) + +-- test with ordered guc disabled +SET timescaledb.enable_ordered_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_1_1_chunk."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=3 loops=1) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(9 rows) + +RESET timescaledb.enable_ordered_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=3 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=3 loops=1) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test with chunk append disabled +SET timescaledb.enable_chunk_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_1_1_chunk."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=3 loops=1) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(9 rows) + +RESET timescaledb.enable_chunk_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=3 loops=1) + Order: metrics."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (actual rows=3 loops=1) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +\set TEST_TABLE 'metrics_space' +\ir :TEST_QUERY_NAME +-- 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. +-- test ASC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test DESC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test query with ORDER BY column not in targetlist +:PREFIX +SELECT pg_typeof(device_id), + pg_typeof(v2) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) +(18 rows) + +-- ORDER BY may include other columns after time column +:PREFIX +SELECT time, + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Index Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Filter: (device_id = 1) +(9 rows) + +-- test RECORD in targetlist +:PREFIX +SELECT (time, + device_id, + v0) +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Index Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Filter: (device_id = 1) +(9 rows) + +-- test sort column not in targetlist +:PREFIX +SELECT time_bucket('1h', time) +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- queries with ORDER BY non-time column shouldn't use ordered append +:PREFIX +SELECT device_id +FROM :TEST_TABLE +ORDER BY device_id +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk.device_id + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_device_id_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_device_id_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_device_id_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(21 rows) + +-- time column must be primary sort order +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY device_id, + time +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_8_chunk.device_id, _hyper_2_8_chunk."time" + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + Heap Fetches: 1 +(21 rows) + +-- test equality constraint on ORDER BY prefix +-- currently not optimized +SET enable_seqscan TO false; +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY device_id, + time +LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Append (actual rows=10 loops=1) + Sort Key: _hyper_2_10_chunk."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=10 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) +(9 rows) + +RESET enable_seqscan; +-- queries without LIMIT should use ordered append +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time ASC; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics_space (actual rows=27348 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=7196 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=3598 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=3598 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 7196 + -> Merge Append (actual rows=10076 loops=1) + Sort Key: _hyper_2_7_chunk."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 10076 + -> Merge Append (actual rows=10076 loops=1) + Sort Key: _hyper_2_10_chunk."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 10076 +(23 rows) + +-- queries without ORDER BY shouldnt use ordered append +:PREFIX +SELECT pg_typeof(time) +FROM :TEST_TABLE +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Seq Scan on _hyper_2_4_chunk (actual rows=1 loops=1) + -> Seq Scan on _hyper_2_5_chunk (never executed) + -> Seq Scan on _hyper_2_6_chunk (never executed) + -> Seq Scan on _hyper_2_7_chunk (never executed) + -> Seq Scan on _hyper_2_8_chunk (never executed) + -> Seq Scan on _hyper_2_9_chunk (never executed) + -> Seq Scan on _hyper_2_10_chunk (never executed) + -> Seq Scan on _hyper_2_11_chunk (never executed) + -> Seq Scan on _hyper_2_12_chunk (never executed) +(12 rows) + +-- test interaction with constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 +(25 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 +(25 rows) + +-- test interaction with runtime exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=0 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=0 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=0 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=0 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 +(36 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Heap Fetches: 0 +(36 rows) + +-- test constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz + AND time < '2000-01-10' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=0 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=0 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=0 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=0 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Index Cond: (("time" > ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 +(25 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz + AND time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: (("time" < ('2000-01-08'::cstring)::timestamp with time zone) AND ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 0 +(25 rows) + +-- min/max queries +:PREFIX +SELECT max(time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------ + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(38 rows) + +:PREFIX +SELECT min(time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(38 rows) + +-- test first/last (doesn't use ordered append yet) +:PREFIX +SELECT first(time, time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------- + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(38 rows) + +:PREFIX +SELECT last(time, time) +FROM :TEST_TABLE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------ + Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 +(38 rows) + +-- test query with time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test query with ORDER BY time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: time_bucket('@ 1 day'::interval, metrics_space."time") + -> Merge Append (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_2_4_chunk."time")) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_2_7_chunk."time")) + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_2_10_chunk."time")) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test query with ORDER BY time_bucket, device_id +-- must not use ordered append +:PREFIX +SELECT time_bucket('1d', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time_bucket('1d', time), + device_id +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_2_8_chunk."time")), _hyper_2_8_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Index Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk (actual rows=5038 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_10_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk (actual rows=5038 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_4_chunk (actual rows=3598 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk (actual rows=3598 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_7_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) +(18 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY date_trunc('day', time) +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: date_trunc('day'::text, metrics_space."time") + -> Merge Append (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_2_4_chunk."time")) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: (date_trunc('day'::text, _hyper_2_7_chunk."time")) + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: (date_trunc('day'::text, _hyper_2_10_chunk."time")) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT date_trunc('day', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: date_trunc('day'::text, metrics_space."time") + -> Merge Append (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_2_4_chunk."time")) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: (date_trunc('day'::text, _hyper_2_7_chunk."time")) + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: (date_trunc('day'::text, _hyper_2_10_chunk."time")) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test query with ORDER BY date_trunc, device_id +-- must not use ordered append +:PREFIX +SELECT date_trunc('day', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY 1, + 2 +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_2_8_chunk."time")), _hyper_2_8_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Index Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk (actual rows=5038 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_10_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk (actual rows=5038 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_4_chunk (actual rows=3598 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Index Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk (actual rows=3598 loops=1) + Index Cond: (device_id = ANY ('{1,2}'::integer[])) + -> Seq Scan on _hyper_2_7_chunk (actual rows=5038 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) +(18 rows) + +-- test query with now() should result in ordered ChunkAppend +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < now() + '1 month' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" < (now() + '@ 1 mon'::interval)) + Heap Fetches: 0 +(36 rows) + +-- test CTE +:PREFIX WITH i AS ( + SELECT time + FROM :TEST_TABLE + WHERE time < now() + ORDER BY time DESC + LIMIT 100 +) +SELECT * +FROM i; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=100 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=100 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=21 loops=1) + Index Cond: ("time" < now()) + Heap Fetches: 21 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=60 loops=1) + Index Cond: ("time" < now()) + Heap Fetches: 60 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=21 loops=1) + Index Cond: ("time" < now()) + Heap Fetches: 21 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" < now()) + Heap Fetches: 0 +(36 rows) + +-- test CTE +-- no chunk exclusion for CTE because cte query is not pulled up +:PREFIX WITH cte AS ( + SELECT time + FROM :TEST_TABLE + WHERE device_id = 1 + ORDER BY time +) +SELECT * +FROM cte +WHERE time < '2000-02-01'::timestamptz; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics_space (actual rows=13674 loops=1) + Order: metrics_space."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=3598 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) +(11 rows) + +-- test subquery +-- not ChunkAppend so no chunk exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time = ( + SELECT max(time) + FROM :TEST_TABLE) +ORDER BY time; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on metrics_space (actual rows=5 loops=1) + InitPlan 2 (returns $1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space metrics_space_1 (actual rows=1 loops=1) + Order: metrics_space_1."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk _hyper_2_12_chunk_1 (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk _hyper_2_11_chunk_1 (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk _hyper_2_10_chunk_1 (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk_1."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk _hyper_2_9_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk _hyper_2_8_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk _hyper_2_7_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk_1."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk _hyper_2_6_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk _hyper_2_5_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk _hyper_2_4_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=1) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=1) + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=0 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 0 + -> Merge Append (actual rows=5 loops=1) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=3 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 3 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" = $1) + Heap Fetches: 1 +(70 rows) + +-- test ordered append with limit expression +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT ( + SELECT length('four')); + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=4 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=4 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=4 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=3 loops=1) + Heap Fetches: 3 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(29 rows) + +-- test with ordered guc disabled +SET timescaledb.enable_ordered_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(21 rows) + +RESET timescaledb.enable_ordered_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=3 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test with chunk append disabled +SET timescaledb.enable_chunk_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 +(21 rows) + +RESET timescaledb.enable_chunk_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=3 loops=1) + Order: metrics_space."time" + -> Merge Append (actual rows=3 loops=1) + Sort Key: _hyper_2_4_chunk."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_7_chunk."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_10_chunk."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +\set TEST_TABLE 'metrics_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test ASC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(11 rows) + +-- test DESC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(11 rows) + +-- test query with ORDER BY column not in targetlist +:PREFIX +SELECT pg_typeof(device_id), + pg_typeof(v2) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(12 rows) + +-- ORDER BY may include other columns after time column +:PREFIX +SELECT time, + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=1 loops=1) + Order: metrics_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (never executed) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (never executed) + Index Cond: (device_id = 1) +(12 rows) + +-- test RECORD in targetlist +:PREFIX +SELECT (time, + device_id, + v0) +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=1 loops=1) + Order: metrics_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (never executed) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (never executed) + Index Cond: (device_id = 1) +(12 rows) + +-- test sort column not in targetlist +:PREFIX +SELECT time_bucket('1h', time) +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(12 rows) + +-- queries with ORDER BY non-time column shouldn't use ordered append +:PREFIX +SELECT device_id +FROM :TEST_TABLE +ORDER BY device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_3_13_chunk.device_id + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_18_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_17_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_16_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(18 rows) + +-- time column must be primary sort order +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY device_id, + time +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_3_13_chunk.device_id, _hyper_3_13_chunk."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_18_chunk.device_id, compress_hyper_4_18_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_17_chunk.device_id, compress_hyper_4_17_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_4_16_chunk.device_id, compress_hyper_4_16_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 +(24 rows) + +-- test equality constraint on ORDER BY prefix +-- currently not optimized +SET enable_seqscan TO false; +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY device_id, + time +LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=10 loops=1) + -> Merge Append (actual rows=10 loops=1) + Sort Key: _hyper_3_13_chunk."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=10 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) +(12 rows) + +RESET enable_seqscan; +-- queries without LIMIT should use ordered append +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time ASC; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Sort (actual rows=27348 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: quicksort + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=7196 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 +(16 rows) + +-- queries without ORDER BY shouldnt use ordered append +:PREFIX +SELECT pg_typeof(time) +FROM :TEST_TABLE +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=1 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=1 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (never executed) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) +(9 rows) + +-- test interaction with constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_14_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=45575 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=20385 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 4615 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=25 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 5 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) +(15 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=45575 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=20385 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 4615 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=25 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 5 +(15 rows) + +-- test interaction with runtime exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=41975 loops=1) + Chunks excluded during startup: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=16785 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 8405 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(13 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=26390 loops=1) + Chunks excluded during startup: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=8400 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 16790 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) +(13 rows) + +-- test constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz + AND time < '2000-01-10' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=7195 loops=1) + Chunks excluded during startup: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=7195 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 12995 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=25 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 5 +(12 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz + AND time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=3595 loops=1) + Chunks excluded during startup: 1 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=3595 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 21405 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=25 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 5 +(12 rows) + +-- min/max queries +:PREFIX +SELECT max(time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(8 rows) + +:PREFIX +SELECT min(time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(8 rows) + +-- test first/last (doesn't use ordered append yet) +:PREFIX +SELECT first(time, time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(8 rows) + +:PREFIX +SELECT last(time, time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(8 rows) + +-- test query with time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(12 rows) + +-- test query with ORDER BY time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_3_13_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(12 rows) + +-- test query with ORDER BY time_bucket, device_id +-- must not use ordered append +:PREFIX +SELECT time_bucket('1d', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time_bucket('1d', time), + device_id +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_3_13_chunk."time")), _hyper_3_13_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=7196 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 +(18 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY date_trunc('day', time) +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_3_13_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(12 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT date_trunc('day', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_3_13_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(12 rows) + +-- test query with ORDER BY date_trunc, device_id +-- must not use ordered append +:PREFIX +SELECT date_trunc('day', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY 1, + 2 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_3_13_chunk."time")), _hyper_3_13_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=7196 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=10076 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=12 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 18 +(18 rows) + +-- test query with now() should result in ordered ChunkAppend +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < now() + '1 month' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_compressed."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=68370 loops=1) + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(15 rows) + +-- test CTE +:PREFIX WITH i AS ( + SELECT time + FROM :TEST_TABLE + WHERE time < now() + ORDER BY time DESC + LIMIT 100 +) +SELECT * +FROM i; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: metrics_compressed."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=68370 loops=1) + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(15 rows) + +-- test CTE +-- no chunk exclusion for CTE because cte query is not pulled up +:PREFIX WITH cte AS ( + SELECT time + FROM :TEST_TABLE + WHERE device_id = 1 + ORDER BY time +) +SELECT * +FROM cte +WHERE time < '2000-02-01'::timestamptz; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (ChunkAppend) on metrics_compressed (actual rows=13674 loops=1) + Order: metrics_compressed."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(17 rows) + +-- test subquery +-- not ChunkAppend so no chunk exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time = ( + SELECT max(time) + FROM :TEST_TABLE) +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Custom Scan (ChunkAppend) on metrics_compressed (actual rows=5 loops=1) + Chunks excluded during runtime: 1 + InitPlan 1 (returns $0) + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk _hyper_3_13_chunk_1 (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk _hyper_3_14_chunk_1 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk _hyper_3_15_chunk_1 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 20 + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=5 loops=1) + Filter: ("time" = $0) + Rows Removed by Filter: 4995 + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=5 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 25 +(26 rows) + +-- test ordered append with limit expression +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT ( + SELECT length('four')); + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=4 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Sort (actual rows=4 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(13 rows) + +-- test with ordered guc disabled +SET timescaledb.enable_ordered_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(11 rows) + +RESET timescaledb.enable_ordered_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(11 rows) + +-- test with chunk append disabled +SET timescaledb.enable_chunk_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(11 rows) + +RESET timescaledb.enable_chunk_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_3_13_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(11 rows) + +\set TEST_TABLE 'metrics_space_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test ASC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(23 rows) + +-- test DESC for ordered chunks +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(23 rows) + +-- test query with ORDER BY column not in targetlist +:PREFIX +SELECT pg_typeof(device_id), + pg_typeof(v2) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(24 rows) + +-- ORDER BY may include other columns after time column +:PREFIX +SELECT time, + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=1 loops=1) + Order: metrics_space_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk._ts_meta_sequence_num + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_33_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_33_chunk (never executed) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_36_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_36_chunk (never executed) + Filter: (device_id = 1) +(19 rows) + +-- test RECORD in targetlist +:PREFIX +SELECT (time, + device_id, + v0) +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC, + device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=1 loops=1) + Order: metrics_space_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk._ts_meta_sequence_num + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_33_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_33_chunk (never executed) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_36_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_36_chunk (never executed) + Filter: (device_id = 1) +(19 rows) + +-- test sort column not in targetlist +:PREFIX +SELECT time_bucket('1h', time) +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(24 rows) + +-- queries with ORDER BY non-time column shouldn't use ordered append +:PREFIX +SELECT device_id +FROM :TEST_TABLE +ORDER BY device_id +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_5_19_chunk.device_id + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_36_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_35_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_34_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_33_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_32_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_31_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_29_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_28_chunk.device_id + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(48 rows) + +-- time column must be primary sort order +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY device_id, + time +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_5_25_chunk.device_id, _hyper_5_25_chunk."time" + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk.device_id, compress_hyper_6_30_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_29_chunk.device_id, compress_hyper_6_29_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_36_chunk.device_id, compress_hyper_6_36_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_32_chunk.device_id, compress_hyper_6_32_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_35_chunk.device_id, compress_hyper_6_35_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 8 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_33_chunk.device_id, compress_hyper_6_33_chunk._ts_meta_sequence_num DESC + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) +(42 rows) + +-- test equality constraint on ORDER BY prefix +-- currently not optimized +SET enable_seqscan TO false; +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY device_id, + time +LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=10 loops=1) + -> Merge Append (actual rows=10 loops=1) + Sort Key: _hyper_5_25_chunk."time" + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=10 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) +(12 rows) + +RESET enable_seqscan; +-- queries without LIMIT should use ordered append +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time ASC; + QUERY PLAN +------------------------------------------------------------------------------------------- + Sort (actual rows=27348 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: quicksort + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 8 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 +(25 rows) + +-- queries without ORDER BY shouldnt use ordered append +:PREFIX +SELECT pg_typeof(time) +FROM :TEST_TABLE +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=1 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=1 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (never executed) + -> Seq Scan on compress_hyper_6_35_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (never executed) + -> Seq Scan on compress_hyper_6_34_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (never executed) + -> Seq Scan on compress_hyper_6_33_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (never executed) + -> Seq Scan on compress_hyper_6_32_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (never executed) + -> Seq Scan on compress_hyper_6_31_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (never executed) + -> Seq Scan on compress_hyper_6_30_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (never executed) + -> Seq Scan on compress_hyper_6_29_chunk (never executed) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (never executed) + -> Seq Scan on compress_hyper_6_28_chunk (never executed) +(21 rows) + +-- test interaction with constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_22_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=45575 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=4077 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 923 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=12231 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 2769 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=4077 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 923 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) +(35 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-07' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=45575 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=4077 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 923 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=12231 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 2769 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=4077 loops=1) + Filter: ("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 923 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 +(35 rows) + +-- test interaction with runtime exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_space_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=41975 loops=1) + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=0 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 3598 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=0 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 10794 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=0 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 3598 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Merge Append (actual rows=16785 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=3357 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 1681 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=10071 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 5043 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=3357 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 1681 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=25190 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + Filter: ("time" > ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(41 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_space_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=26390 loops=1) + -> Merge Append (actual rows=17990 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Merge Append (actual rows=8400 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=1680 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 3358 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=5040 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 10074 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=1680 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 3358 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=0 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 5038 + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=0 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 15114 + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=0 loops=1) + Filter: ("time" < ('2000-01-08'::cstring)::timestamp with time zone) + Rows Removed by Filter: 5038 + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(41 rows) + +-- test constraint exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time > '2000-01-08'::text::timestamptz + AND time < '2000-01-10' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_space_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=7195 loops=1) + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=0 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 3598 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=0 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 10794 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=0 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 3598 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Merge Append (actual rows=7195 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=1439 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 2599 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=4317 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 7797 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=1439 loops=1) + Filter: (("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) AND ("time" > ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 2599 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_min_1 < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 +(40 rows) + +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < '2000-01-08'::text::timestamptz + AND time > '2000-01-07' +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_space_compressed."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=3595 loops=1) + -> Merge Append (actual rows=3595 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=719 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 4281 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=2157 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 12843 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=719 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 4281 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=5 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1 + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=0 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 5038 + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=0 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 15114 + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=0 loops=1) + Filter: (("time" > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) AND ("time" < ('2000-01-08'::cstring)::timestamp with time zone)) + Rows Removed by Filter: 5038 + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + Filter: (_ts_meta_max_1 > 'Fri Jan 07 00:00:00 2000 PST'::timestamp with time zone) +(40 rows) + +-- min/max queries +:PREFIX +SELECT max(time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(20 rows) + +:PREFIX +SELECT min(time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(20 rows) + +-- test first/last (doesn't use ordered append yet) +:PREFIX +SELECT first(time, time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(20 rows) + +:PREFIX +SELECT last(time, time) +FROM :TEST_TABLE; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(20 rows) + +-- test query with time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY time ASC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(24 rows) + +-- test query with ORDER BY time_bucket +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_5_19_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(24 rows) + +-- test query with ORDER BY time_bucket, device_id +-- must not use ordered append +:PREFIX +SELECT time_bucket('1d', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY time_bucket('1d', time), + device_id +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (time_bucket('@ 1 day'::interval, _hyper_5_25_chunk."time")), _hyper_5_25_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 8 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) +(27 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT time_bucket('1d', time) +FROM :TEST_TABLE +ORDER BY date_trunc('day', time) +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_5_19_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(24 rows) + +-- test query with ORDER BY date_trunc +:PREFIX +SELECT date_trunc('day', time) +FROM :TEST_TABLE +ORDER BY 1 +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_5_19_chunk."time")) + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(24 rows) + +-- test query with ORDER BY date_trunc, device_id +-- must not use ordered append +:PREFIX +SELECT date_trunc('day', time), + device_id, + v0 +FROM :TEST_TABLE +WHERE device_id IN (1, 2) +ORDER BY 1, + 2 +LIMIT 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: (date_trunc('day'::text, _hyper_5_25_chunk."time")), _hyper_5_25_chunk.device_id + Sort Method: top-N heapsort + -> Result (actual rows=27348 loops=1) + -> Append (actual rows=27348 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) + Rows Removed by Filter: 8 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Filter: (device_id = ANY ('{1,2}'::integer[])) +(27 rows) + +-- test query with now() should result in ordered ChunkAppend +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time < now() + '1 month' +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: metrics_space_compressed."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=68370 loops=1) + -> Merge Append (actual rows=25190 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=25190 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=17990 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + Filter: ("time" < (now() + '@ 1 mon'::interval)) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(35 rows) + +-- test CTE +:PREFIX WITH i AS ( + SELECT time + FROM :TEST_TABLE + WHERE time < now() + ORDER BY time DESC + LIMIT 100 +) +SELECT * +FROM i; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: metrics_space_compressed."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=68370 loops=1) + -> Merge Append (actual rows=25190 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=25190 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Merge Append (actual rows=17990 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + Filter: ("time" < now()) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(35 rows) + +-- test CTE +-- no chunk exclusion for CTE because cte query is not pulled up +:PREFIX WITH cte AS ( + SELECT time + FROM :TEST_TABLE + WHERE device_id = 1 + ORDER BY time +) +SELECT * +FROM cte +WHERE time < '2000-02-01'::timestamptz; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------ + Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=13674 loops=1) + Order: metrics_space_compressed."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(17 rows) + +-- test subquery +-- not ChunkAppend so no chunk exclusion +:PREFIX +SELECT time +FROM :TEST_TABLE +WHERE time = ( + SELECT max(time) + FROM :TEST_TABLE) +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=5 loops=1) + InitPlan 1 (returns $0) + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk _hyper_5_19_chunk_1 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk _hyper_5_20_chunk_1 (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk compress_hyper_6_35_chunk_1 (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk _hyper_5_21_chunk_1 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk compress_hyper_6_34_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk _hyper_5_22_chunk_1 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk _hyper_5_23_chunk_1 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk compress_hyper_6_32_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk _hyper_5_24_chunk_1 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk compress_hyper_6_31_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk _hyper_5_25_chunk_1 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk _hyper_5_26_chunk_1 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk compress_hyper_6_29_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk _hyper_5_27_chunk_1 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk compress_hyper_6_28_chunk_1 (actual rows=6 loops=1) + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 4 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 4 + -> Merge Append (actual rows=0 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=0 loops=1) + Filter: ("time" = $0) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=5 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + Filter: ("time" = $0) + Rows Removed by Filter: 999 + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=1 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 5 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=3 loops=1) + Filter: ("time" = $0) + Rows Removed by Filter: 2997 + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=3 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 15 + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=1 loops=1) + Filter: ("time" = $0) + Rows Removed by Filter: 999 + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=1 loops=1) + Filter: ((_ts_meta_min_1 <= $0) AND (_ts_meta_max_1 >= $0)) + Rows Removed by Filter: 5 +(73 rows) + +-- test ordered append with limit expression +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT ( + SELECT length('four')); + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=4 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Sort (actual rows=4 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(25 rows) + +-- test with ordered guc disabled +SET timescaledb.enable_ordered_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(23 rows) + +RESET timescaledb.enable_ordered_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(23 rows) + +-- test with chunk append disabled +SET timescaledb.enable_chunk_append TO OFF; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(23 rows) + +RESET timescaledb.enable_chunk_append; +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time +LIMIT 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=3 loops=1) + -> Sort (actual rows=3 loops=1) + Sort Key: _hyper_5_19_chunk."time" + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(23 rows) + +-- get results for all the queries +-- run queries on uncompressed hypertable and store result +\set PREFIX '' +\set PREFIX_VERBOSE '' +\set ECHO none diff --git a/tsl/test/shared/expected/ordered_append_join.out b/tsl/test/shared/expected/ordered_append_join-11.out similarity index 100% rename from tsl/test/shared/expected/ordered_append_join.out rename to tsl/test/shared/expected/ordered_append_join-11.out diff --git a/tsl/test/shared/expected/ordered_append_join-12.out b/tsl/test/shared/expected/ordered_append_join-12.out new file mode 100644 index 00000000000..22562dbbf7e --- /dev/null +++ b/tsl/test/shared/expected/ordered_append_join-12.out @@ -0,0 +1,3862 @@ +-- 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('include/%s.sql', :'TEST_BASE_NAME') as "TEST_QUERY_NAME", + format('%s/shared/results/%s_results_uncompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_UNCOMPRESSED", + format('%s/shared/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESSED" +\gset +SELECT format('\! diff -u --label "Uncompressed results" --label "Compressed results" %s %s', :'TEST_RESULTS_UNCOMPRESSED', :'TEST_RESULTS_COMPRESSED') as "DIFF_CMD" +\gset +-- get EXPLAIN output for all variations +\set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)' +\set PREFIX_VERBOSE 'EXPLAIN (analyze, costs off, timing off, summary off, verbose)' +set work_mem to '64MB'; +set max_parallel_workers_per_gather to 0; +\set TEST_TABLE 'metrics' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Nested Loop (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 + -> Materialize (actual rows=2 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(12 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=2 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(13 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(12 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_3 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 +(16 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------ + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=2) + Order: o."time" + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_3 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 +(16 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_3 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 +(16 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +---------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=0 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 3 +(6 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 +(25 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=2 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o1_1 (actual rows=2 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Materialize (actual rows=10 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=6 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o2_1 (actual rows=6 loops=1) + Heap Fetches: 6 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Heap Fetches: 0 +(20 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Sort (actual rows=1 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Nested Loop (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Append (actual rows=1 loops=1) + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_2 (actual rows=1 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 1 +(28 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + Join Filter: (o1.device_id = o2.device_id) + Rows Removed by Join Filter: 400 + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o1_2 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o1_3 (never executed) + -> Materialize (actual rows=500 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=101 loops=1) + Order: o2."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o2_1 (actual rows=101 loops=1) + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o2_2 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o2_3 (never executed) +(16 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=1 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_1 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics o3 (actual rows=100 loops=1) + Order: o3."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o3_1 (actual rows=100 loops=1) + Index Cond: (device_id = 3) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o3_2 (never executed) + Index Cond: (device_id = 3) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o3_3 (never executed) + Index Cond: (device_id = 3) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(40 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_space' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Nested Loop (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 + -> Materialize (actual rows=2 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(30 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=2 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=2 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(31 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Index Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Filter: (device_id = 1) +(9 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 +(38 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=2) + Order: o."time" + -> Merge Append (actual rows=0 loops=2) + Sort Key: o_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_1 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_2 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_3 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=2) + Sort Key: o_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_4 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_6 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Merge Append (never executed) + Sort Key: o_7."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_7 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_8 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_9 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 +(38 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 +(38 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=0 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 +(38 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=3598 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 +(25 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=2 loops=1) + Order: o1."time" + -> Merge Append (actual rows=2 loops=1) + Sort Key: o1_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=2 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o1_2 (actual rows=1 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o1_3 (actual rows=1 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: o1_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_4 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o1_5 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o1_6 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Materialize (actual rows=10 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=6 loops=1) + Order: o2."time" + -> Merge Append (actual rows=6 loops=1) + Sort Key: o2_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o2_1 (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o2_2 (actual rows=4 loops=1) + Heap Fetches: 4 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o2_3 (actual rows=2 loops=1) + Heap Fetches: 2 + -> Merge Append (never executed) + Sort Key: o2_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o2_4 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o2_5 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o2_6 (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: o2_7."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o2_7 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o2_8 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o2_9 (never executed) + Heap Fetches: 0 +(54 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort (actual rows=1 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Nested Loop (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Append (actual rows=1 loops=1) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk o1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk o1_1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk o1_2 (actual rows=1 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 1 +(52 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + Join Filter: (o1.device_id = o2.device_id) + Rows Removed by Join Filter: 400 + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Merge Append (actual rows=100 loops=1) + Sort Key: o1_1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=21 loops=1) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o1_2 (actual rows=60 loops=1) + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o1_3 (actual rows=21 loops=1) + -> Merge Append (never executed) + Sort Key: o1_4."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_4 (never executed) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o1_5 (never executed) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o1_6 (never executed) + -> Merge Append (never executed) + Sort Key: o1_7."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_7 (never executed) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o1_8 (never executed) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o1_9 (never executed) + -> Materialize (actual rows=500 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=101 loops=1) + Order: o2."time" + -> Merge Append (actual rows=101 loops=1) + Sort Key: o2_1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o2_1 (actual rows=21 loops=1) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o2_2 (actual rows=61 loops=1) + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o2_3 (actual rows=21 loops=1) + -> Merge Append (never executed) + Sort Key: o2_4."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o2_4 (never executed) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o2_5 (never executed) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o2_6 (never executed) + -> Merge Append (never executed) + Sort Key: o2_7."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o2_7 (never executed) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o2_8 (never executed) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o2_9 (never executed) +(40 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=1 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk o2 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk o2_1 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk o2_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(21 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_space o3 (actual rows=100 loops=1) + Order: o3."time" + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o3_1 (actual rows=100 loops=1) + Filter: (device_id = 3) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o3_2 (never executed) + Filter: (device_id = 3) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o3_3 (never executed) + Filter: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(34 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Nested Loop (actual rows=136740 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Materialize (actual rows=2 loops=68370) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(14 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(16 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=1 loops=1) + Order: metrics_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (never executed) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (never executed) + Index Cond: (device_id = 1) +(12 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(11 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=3) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_3 (actual rows=3600 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4063 + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 +(23 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Sort (actual rows=1 loops=2) + Sort Key: o."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=2) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_18_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (actual rows=3600 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 3900 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=8 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 22 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_3 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) +(23 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=3) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_3 (actual rows=3600 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 4063 + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 +(23 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Sort (actual rows=0 loops=3) + Sort Key: o."time" DESC + Sort Method: quicksort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=0 loops=3) + Chunks excluded during startup: 3 +(8 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=4 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(37 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Sort (actual rows=2 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Append (actual rows=26390 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1 (actual rows=17990 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_1 (actual rows=8400 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1790 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 15 + -> Materialize (actual rows=10 loops=1) + -> Sort (actual rows=6 loops=1) + Sort Key: o2."time" + Sort Method: quicksort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2 (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_1 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_2 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(29 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=1 loops=1) + Merge Cond: (o1."time" = (max(_hyper_3_13_chunk."time"))) + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=3598 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Sort (actual rows=1 loops=1) + Sort Key: (max(_hyper_3_13_chunk."time")) + Sort Method: quicksort + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=30 loops=1) +(24 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: o1."time" + Sort Method: top-N heapsort + -> Hash Join (actual rows=68370 loops=1) + Hash Cond: ((o1.device_id = o2.device_id) AND (o1."time" = o2."time")) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1 (actual rows=17990 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_1 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_2 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Hash (actual rows=68370 loops=1) + Buckets: 131072 Batches: 1 + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2 (actual rows=17990 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_1 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_2 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=30 loops=1) +(22 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=1 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2 (actual rows=100 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_1 (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_2 (never executed) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 1) +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o3 (actual rows=100 loops=1) + Order: o3."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o3_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_2 (actual rows=1 loops=1) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o3_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_2 (never executed) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o3_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_2 (never executed) + Index Cond: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(40 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_space_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Nested Loop (actual rows=136740 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Materialize (actual rows=2 loops=68370) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(26 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(28 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=1 loops=1) + Order: metrics_space_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk._ts_meta_sequence_num + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_33_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_33_chunk (never executed) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_36_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_36_chunk (never executed) + Filter: (device_id = 1) +(19 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(23 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=3600 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=2160 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2438 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Sort (actual rows=1 loops=2) + Sort Key: o."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=2) + -> Merge Append (actual rows=0 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_1 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_2 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_3 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Merge Append (actual rows=3600 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_4 (actual rows=720 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 780 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=2 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=2160 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2340 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=4 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 14 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_6 (actual rows=720 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 780 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=2 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Merge Append (actual rows=0 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_7 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_8 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_9 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 +(58 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=3600 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=2160 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 2438 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Sort (actual rows=0 loops=3) + Sort Key: o."time" DESC + Sort Method: quicksort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=0 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 1533 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 4598 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 1533 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(37 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Sort (actual rows=2 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Append (actual rows=26390 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1 (actual rows=3598 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o1_1 (actual rows=10794 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o1_2 (actual rows=3598 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_3 (actual rows=1680 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 358 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=3 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o1_4 (actual rows=5040 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1074 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=9 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 9 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o1_5 (actual rows=1680 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 358 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=3 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Materialize (actual rows=10 loops=1) + -> Sort (actual rows=6 loops=1) + Sort Key: o2."time" + Sort Method: quicksort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk compress_hyper_6_35_chunk_1 (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o2_2 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk compress_hyper_6_34_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_3 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_4 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk compress_hyper_6_32_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o2_5 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk compress_hyper_6_31_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2_6 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_7 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o2_8 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(61 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=1 loops=1) + Merge Cond: (o1."time" = (max(_hyper_5_19_chunk."time"))) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Sort (actual rows=1 loops=1) + Sort Key: (max(_hyper_5_19_chunk."time")) + Sort Method: quicksort + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(36 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: o1."time" + Sort Method: top-N heapsort + -> Hash Join (actual rows=68370 loops=1) + Hash Cond: ((o1.device_id = o2.device_id) AND (o1."time" = o2."time")) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o1_1 (actual rows=10794 loops=1) + -> Index Scan using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o1_2 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_3 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o1_4 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o1_5 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_6 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o1_7 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o1_8 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Hash (actual rows=68370 loops=1) + Buckets: 131072 Batches: 1 + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=10794 loops=1) + -> Index Scan using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk compress_hyper_6_35_chunk_1 (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o2_2 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk compress_hyper_6_34_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_3 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_4 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk compress_hyper_6_32_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o2_5 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk compress_hyper_6_31_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2_6 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_7 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk compress_hyper_6_29_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o2_8 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk compress_hyper_6_28_chunk_1 (actual rows=6 loops=1) +(46 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=1 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2 (actual rows=100 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2_1 (never executed) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_2 (never executed) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (never executed) + Index Cond: (device_id = 1) +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o3 (actual rows=100 loops=1) + Order: o3."time" + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o3_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o3_2 (never executed) + -> Index Scan Backward using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk (never executed) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o3_3 (never executed) + -> Index Scan Backward using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk (never executed) + Index Cond: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(40 rows) + +RESET enable_seqscan; +-- get results for all the queries +-- run queries on uncompressed hypertable and store result +\set PREFIX '' +\set PREFIX_VERBOSE '' +\set ECHO none diff --git a/tsl/test/shared/expected/ordered_append_join-13.out b/tsl/test/shared/expected/ordered_append_join-13.out new file mode 100644 index 00000000000..cbc2bf6d419 --- /dev/null +++ b/tsl/test/shared/expected/ordered_append_join-13.out @@ -0,0 +1,3862 @@ +-- 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('include/%s.sql', :'TEST_BASE_NAME') as "TEST_QUERY_NAME", + format('%s/shared/results/%s_results_uncompressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_UNCOMPRESSED", + format('%s/shared/results/%s_results_compressed.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_COMPRESSED" +\gset +SELECT format('\! diff -u --label "Uncompressed results" --label "Compressed results" %s %s', :'TEST_RESULTS_UNCOMPRESSED', :'TEST_RESULTS_COMPRESSED') as "DIFF_CMD" +\gset +-- get EXPLAIN output for all variations +\set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)' +\set PREFIX_VERBOSE 'EXPLAIN (analyze, costs off, timing off, summary off, verbose)' +set work_mem to '64MB'; +set max_parallel_workers_per_gather to 0; +\set TEST_TABLE 'metrics' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Nested Loop (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 + -> Materialize (actual rows=2 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(12 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=2 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(13 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(12 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Heap Fetches: 0 +(9 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_3 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 +(16 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------ + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=2) + Order: o."time" + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_3 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 +(16 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=1 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o_1 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o_2 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o_3 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 +(16 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +---------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Custom Scan (ChunkAppend) on metrics o (actual rows=0 loops=3) + Order: o."time" DESC + Chunks excluded during startup: 3 +(6 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 1) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 +(25 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=2 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o1_1 (actual rows=2 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Materialize (actual rows=10 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=6 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o2_1 (actual rows=6 loops=1) + Heap Fetches: 6 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Heap Fetches: 0 +(20 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Sort (actual rows=1 loops=1) + Sort Key: o1_1."time" + Sort Method: quicksort + -> Nested Loop (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics (actual rows=1 loops=1) + Order: metrics."time" DESC + -> Index Only Scan using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Append (actual rows=1 loops=1) + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (actual rows=1 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 1 +(28 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + Join Filter: (o1.device_id = o2.device_id) + Rows Removed by Join Filter: 400 + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o1_2 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o1_3 (never executed) + -> Materialize (actual rows=500 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=101 loops=1) + Order: o2."time" + -> Index Scan Backward using _hyper_1_1_chunk_metrics_time_idx on _hyper_1_1_chunk o2_1 (actual rows=101 loops=1) + -> Index Scan Backward using _hyper_1_2_chunk_metrics_time_idx on _hyper_1_2_chunk o2_2 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_metrics_time_idx on _hyper_1_3_chunk o2_3 (never executed) +(16 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=1 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Index Only Scan using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics o3 (actual rows=100 loops=1) + Order: o3."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o3_1 (actual rows=100 loops=1) + Index Cond: (device_id = 3) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o3_2 (never executed) + Index Cond: (device_id = 3) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o3_3 (never executed) + Index Cond: (device_id = 3) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o1_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o1_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o1_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan Backward using _hyper_1_1_chunk_metrics_device_id_time_idx on _hyper_1_1_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan Backward using _hyper_1_2_chunk_metrics_device_id_time_idx on _hyper_1_2_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_1_3_chunk_metrics_device_id_time_idx on _hyper_1_3_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(40 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_space' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Nested Loop (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 + -> Materialize (actual rows=2 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(30 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=2 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=2 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(31 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Index Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Filter: (device_id = 1) + -> Index Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Filter: (device_id = 1) +(9 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Heap Fetches: 0 +(27 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 3 +(38 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=2) + Order: o."time" + -> Merge Append (actual rows=0 loops=2) + Sort Key: o_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_1 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_2 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_3 (actual rows=0 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=2) + Sort Key: o_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_4 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_6 (actual rows=1 loops=2) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 2 + -> Merge Append (never executed) + Sort Key: o_7."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_7 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_8 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_9 (never executed) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Heap Fetches: 0 +(38 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=1 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 0 + -> Merge Append (actual rows=1 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=1 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Heap Fetches: 3 +(38 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Custom Scan (ChunkAppend) on metrics_space o (actual rows=0 loops=3) + Order: o."time" DESC + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_1."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o_1 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o_2 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o_3 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_4."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o_4 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o_5 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o_6 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Merge Append (actual rows=0 loops=3) + Sort Key: o_7."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o_7 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o_8 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o_9 (actual rows=0 loops=3) + Index Cond: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Heap Fetches: 0 +(38 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=3598 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (actual rows=5038 loops=1) + Index Cond: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + Filter: (device_id = 1) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=3598 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 3598 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (actual rows=5038 loops=1) + Index Cond: ((device_id = 2) AND ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone)) + Heap Fetches: 5038 +(25 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=2 loops=1) + Order: o1."time" + -> Merge Append (actual rows=2 loops=1) + Sort Key: o1_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=2 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o1_2 (actual rows=1 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o1_3 (actual rows=1 loops=1) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: o1_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_4 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o1_5 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o1_6 (never executed) + Index Cond: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Heap Fetches: 0 + -> Materialize (actual rows=10 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=6 loops=1) + Order: o2."time" + -> Merge Append (actual rows=6 loops=1) + Sort Key: o2_1."time" + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o2_1 (actual rows=2 loops=1) + Heap Fetches: 2 + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o2_2 (actual rows=4 loops=1) + Heap Fetches: 4 + -> Index Only Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o2_3 (actual rows=2 loops=1) + Heap Fetches: 2 + -> Merge Append (never executed) + Sort Key: o2_4."time" + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o2_4 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o2_5 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o2_6 (never executed) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: o2_7."time" + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o2_7 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o2_8 (never executed) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o2_9 (never executed) + Heap Fetches: 0 +(54 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort (actual rows=1 loops=1) + Sort Key: o1_1."time" + Sort Method: quicksort + -> Nested Loop (actual rows=1 loops=1) + -> Result (actual rows=1 loops=1) + InitPlan 1 (returns $0) + -> Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space (actual rows=1 loops=1) + Order: metrics_space."time" DESC + -> Merge Append (actual rows=1 loops=1) + Sort Key: _hyper_2_12_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Index Only Scan Backward using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk (actual rows=1 loops=1) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 1 + -> Merge Append (never executed) + Sort Key: _hyper_2_9_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Merge Append (never executed) + Sort Key: _hyper_2_6_chunk."time" DESC + -> Index Only Scan Backward using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Index Only Scan Backward using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + Heap Fetches: 0 + -> Append (actual rows=1 loops=1) + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk o1_1 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk o1_2 (actual rows=0 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk o1_3 (actual rows=1 loops=1) + Index Cond: ((device_id = 1) AND ("time" = ($0))) + Heap Fetches: 1 +(52 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + Join Filter: (o1.device_id = o2.device_id) + Rows Removed by Join Filter: 400 + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Merge Append (actual rows=100 loops=1) + Sort Key: o1_1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=21 loops=1) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o1_2 (actual rows=60 loops=1) + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o1_3 (actual rows=21 loops=1) + -> Merge Append (never executed) + Sort Key: o1_4."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_4 (never executed) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o1_5 (never executed) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o1_6 (never executed) + -> Merge Append (never executed) + Sort Key: o1_7."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_7 (never executed) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o1_8 (never executed) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o1_9 (never executed) + -> Materialize (actual rows=500 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=101 loops=1) + Order: o2."time" + -> Merge Append (actual rows=101 loops=1) + Sort Key: o2_1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o2_1 (actual rows=21 loops=1) + -> Index Scan using _hyper_2_5_chunk_metrics_space_time_idx on _hyper_2_5_chunk o2_2 (actual rows=61 loops=1) + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o2_3 (actual rows=21 loops=1) + -> Merge Append (never executed) + Sort Key: o2_4."time" + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o2_4 (never executed) + -> Index Scan using _hyper_2_8_chunk_metrics_space_time_idx on _hyper_2_8_chunk o2_5 (never executed) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o2_6 (never executed) + -> Merge Append (never executed) + Sort Key: o2_7."time" + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o2_7 (never executed) + -> Index Scan using _hyper_2_11_chunk_metrics_space_time_idx on _hyper_2_11_chunk o2_8 (never executed) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o2_9 (never executed) +(40 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=1 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=1 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Index Only Scan using _hyper_2_10_chunk_metrics_space_device_id_time_idx on _hyper_2_10_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 1) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_4_chunk_metrics_space_device_id_time_idx on _hyper_2_4_chunk o2_2 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_7_chunk_metrics_space_device_id_time_idx on _hyper_2_7_chunk o2_3 (never executed) + Index Cond: (device_id = 1) + Heap Fetches: 0 +(21 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(23 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_space o3 (actual rows=100 loops=1) + Order: o3."time" + -> Index Scan using _hyper_2_6_chunk_metrics_space_time_idx on _hyper_2_6_chunk o3_1 (actual rows=100 loops=1) + Filter: (device_id = 3) + -> Index Scan using _hyper_2_9_chunk_metrics_space_time_idx on _hyper_2_9_chunk o3_2 (never executed) + Filter: (device_id = 3) + -> Index Scan using _hyper_2_12_chunk_metrics_space_time_idx on _hyper_2_12_chunk o3_3 (never executed) + Filter: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space o1 (actual rows=100 loops=1) + Order: o1."time" + -> Index Scan using _hyper_2_4_chunk_metrics_space_time_idx on _hyper_2_4_chunk o1_1 (actual rows=100 loops=1) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_7_chunk_metrics_space_time_idx on _hyper_2_7_chunk o1_2 (never executed) + Filter: (device_id = 1) + -> Index Scan using _hyper_2_10_chunk_metrics_space_time_idx on _hyper_2_10_chunk o1_3 (never executed) + Filter: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space o2 (actual rows=100 loops=1) + Order: o2."time" + -> Index Only Scan using _hyper_2_5_chunk_metrics_space_device_id_time_idx on _hyper_2_5_chunk o2_1 (actual rows=100 loops=1) + Index Cond: (device_id = 2) + Heap Fetches: 100 + -> Index Only Scan using _hyper_2_8_chunk_metrics_space_device_id_time_idx on _hyper_2_8_chunk o2_2 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 + -> Index Only Scan using _hyper_2_11_chunk_metrics_space_device_id_time_idx on _hyper_2_11_chunk o2_3 (never executed) + Index Cond: (device_id = 2) + Heap Fetches: 0 +(34 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Nested Loop (actual rows=136740 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Materialize (actual rows=2 loops=68370) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(14 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(16 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed (actual rows=1 loops=1) + Order: metrics_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=1 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (never executed) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (never executed) + Index Cond: (device_id = 1) +(12 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_3_15_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) +(11 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=3) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_3 (actual rows=3600 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4063 + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 +(23 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Sort (actual rows=1 loops=2) + Sort Key: o."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=2) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_18_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (actual rows=3600 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 3900 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=8 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 22 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_3 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) +(23 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=3600 loops=3) + Chunks excluded during startup: 0 + Chunks excluded during runtime: 2 + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o_1 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_4_16_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o_2 (never executed) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_4_17_chunk (never executed) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o_3 (actual rows=3600 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 4063 + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=8 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 +(23 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +--------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Sort (actual rows=0 loops=3) + Sort Key: o."time" DESC + Sort Method: quicksort + -> Custom Scan (ChunkAppend) on metrics_compressed o (actual rows=0 loops=3) + Chunks excluded during startup: 3 +(8 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=4 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(37 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1_1."time" = o2_1."time") + -> Sort (actual rows=2 loops=1) + Sort Key: o1_1."time" + Sort Method: quicksort + -> Append (actual rows=26390 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=17990 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_4_18_chunk (actual rows=20 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=8400 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1790 + -> Seq Scan on compress_hyper_4_17_chunk (actual rows=15 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 15 + -> Materialize (actual rows=10 loops=1) + -> Sort (actual rows=6 loops=1) + Sort Key: o2_1."time" + Sort Method: quicksort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk (actual rows=30 loops=1) +(29 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=1 loops=1) + Merge Cond: (o1."time" = (max(_hyper_3_13_chunk."time"))) + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=3598 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Sort (actual rows=1 loops=1) + Sort Key: (max(_hyper_3_13_chunk."time")) + Sort Method: quicksort + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk (actual rows=17990 loops=1) + -> Seq Scan on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk (actual rows=25190 loops=1) + -> Seq Scan on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=30 loops=1) +(24 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: o1_1."time" + Sort Method: top-N heapsort + -> Hash Join (actual rows=68370 loops=1) + Hash Cond: ((o1_1.device_id = o2_1.device_id) AND (o1_1."time" = o2_1."time")) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=17990 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (actual rows=30 loops=1) + -> Hash (actual rows=68370 loops=1) + Buckets: 131072 Batches: 1 + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=17990 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=20 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (actual rows=30 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (actual rows=25190 loops=1) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (actual rows=30 loops=1) +(22 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=1 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 1) +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o3 (actual rows=100 loops=1) + Order: o3."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o3_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_2 (actual rows=1 loops=1) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o3_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_2 (never executed) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o3_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_2 (never executed) + Index Cond: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_3_13_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_4_18_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_18_chunk compress_hyper_4_18_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_14_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_4_17_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_17_chunk compress_hyper_4_17_chunk_1 (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_3_15_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_4_16_chunk__compressed_hypertable_4_device_id__t on compress_hyper_4_16_chunk compress_hyper_4_16_chunk_1 (never executed) + Index Cond: (device_id = 2) +(40 rows) + +RESET enable_seqscan; +\set TEST_TABLE 'metrics_space_compressed' +\ir :TEST_QUERY_NAME +-- 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. +-- test LATERAL with ordered append in the outer query +:PREFIX +SELECT time, + pg_typeof(l) +FROM :TEST_TABLE, + LATERAL ( + SELECT * + FROM ( + VALUES (1), + (2)) v) l +ORDER BY time DESC +LIMIT 2; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Nested Loop (actual rows=136740 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Materialize (actual rows=2 loops=68370) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(26 rows) + +-- test LATERAL with ordered append in the lateral query +:PREFIX +SELECT time, + pg_typeof(v) +FROM ( + VALUES (1), + (2)) v, + LATERAL ( + SELECT * + FROM :TEST_TABLE + ORDER BY time DESC + LIMIT 2) l; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Nested Loop (actual rows=4 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Subquery Scan on l (actual rows=2 loops=1) + -> Limit (actual rows=2 loops=1) + -> Sort (actual rows=2 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(28 rows) + +-- test plan with best index is chosen +-- this should use device_id, time index +:PREFIX +SELECT time, + device_id +FROM :TEST_TABLE +WHERE device_id = 1 +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=1 loops=1) + Order: metrics_space_compressed."time" DESC + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: compress_hyper_6_30_chunk._ts_meta_sequence_num + Sort Method: quicksort + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_33_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_33_chunk (never executed) + Filter: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (never executed) + -> Sort (never executed) + Sort Key: compress_hyper_6_36_chunk._ts_meta_sequence_num + -> Seq Scan on compress_hyper_6_36_chunk (never executed) + Filter: (device_id = 1) +(19 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX +SELECT time +FROM :TEST_TABLE +ORDER BY time DESC +LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Sort (actual rows=1 loops=1) + Sort Key: _hyper_5_27_chunk."time" DESC + Sort Method: top-N heapsort + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) +(23 rows) + +-- test LATERAL with correlated query +-- only last chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=3600 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=2160 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2438 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test LATERAL with correlated query +-- only 2nd chunk should be executed +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-10'::timestamptz, '2000-01-11', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + ORDER BY time + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=2 loops=1) + -> Function Scan on generate_series g (actual rows=2 loops=1) + -> Limit (actual rows=1 loops=2) + -> Sort (actual rows=1 loops=2) + Sort Key: o."time" + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=2) + -> Merge Append (actual rows=0 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_1 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_2 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 12 + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_3 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Merge Append (actual rows=3600 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_4 (actual rows=720 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 780 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=2 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=2160 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2340 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=4 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 14 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_6 (actual rows=720 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 780 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=2 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 4 + -> Merge Append (actual rows=0 loops=2) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_7 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_8 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_9 (actual rows=0 loops=2) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval))) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=2) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 +(58 rows) + +-- test startup and runtime exclusion together +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time < now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=1 loops=3) + -> Sort (actual rows=1 loops=3) + Sort Key: o."time" DESC + Sort Method: top-N heapsort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=3600 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=3600 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=2160 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 2438 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=720 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" < now())) + Rows Removed by Filter: 813 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test startup and runtime exclusion together +-- all chunks should be filtered +:PREFIX +SELECT g.time, + l.time +FROM generate_series('2000-01-01'::timestamptz, '2000-01-03', '1d') AS g (time) + LEFT OUTER JOIN LATERAL ( + SELECT * + FROM :TEST_TABLE o + WHERE o.time >= g.time + AND o.time < g.time + '1d'::interval + AND o.time > now() + ORDER BY time DESC + LIMIT 1) l ON TRUE; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Nested Loop Left Join (actual rows=3 loops=1) + -> Function Scan on generate_series g (actual rows=3 loops=1) + -> Limit (actual rows=0 loops=3) + -> Sort (actual rows=0 loops=3) + Sort Key: o."time" DESC + Sort Method: quicksort + -> Custom Scan (ChunkAppend) on metrics_space_compressed o (actual rows=0 loops=3) + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o_1 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o_2 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o_3 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o_4 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o_5 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 18 + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o_6 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=0 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 6 + -> Merge Append (actual rows=0 loops=3) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o_7 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 1533 + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o_8 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 4598 + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=5 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 7 + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o_9 (actual rows=0 loops=3) + Filter: (("time" >= g."time") AND ("time" < (g."time" + '@ 1 day'::interval)) AND ("time" > now())) + Rows Removed by Filter: 1533 + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=2 loops=3) + Filter: ((_ts_meta_max_1 >= g."time") AND (_ts_meta_min_1 < (g."time" + '@ 1 day'::interval))) + Rows Removed by Filter: 2 +(58 rows) + +-- test JOIN +-- no exclusion on joined table because quals are not propagated yet +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.time < '2000-02-01' + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=13674 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Materialize (actual rows=13674 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=13674 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (actual rows=5038 loops=1) + Filter: ("time" < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 2) + Filter: (_ts_meta_min_1 < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) +(37 rows) + +-- test JOIN +-- last chunk of o2 should not be executed +:PREFIX +SELECT o1.time, + o2.time +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT * + FROM :TEST_TABLE o2 + ORDER BY time) o2 ON o1.time = o2.time +WHERE o1.time < '2000-01-08' +ORDER BY o1.time +LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=10 loops=1) + -> Merge Join (actual rows=10 loops=1) + Merge Cond: (o1_1."time" = o2_1."time") + -> Sort (actual rows=2 loops=1) + Sort Key: o1_1."time" + Sort Method: quicksort + -> Append (actual rows=26390 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o1_2 (actual rows=10794 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o1_3 (actual rows=3598 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_4 (actual rows=1680 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 358 + -> Seq Scan on compress_hyper_6_33_chunk (actual rows=3 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o1_5 (actual rows=5040 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 1074 + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=9 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 9 + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o1_6 (actual rows=1680 loops=1) + Filter: ("time" < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 358 + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=3 loops=1) + Filter: (_ts_meta_min_1 < 'Sat Jan 08 00:00:00 2000 PST'::timestamp with time zone) + Rows Removed by Filter: 3 + -> Materialize (actual rows=10 loops=1) + -> Sort (actual rows=6 loops=1) + Sort Key: o2_1."time" + Sort Method: quicksort + -> Result (actual rows=68370 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2_1 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_2 (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk compress_hyper_6_35_chunk_1 (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o2_3 (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk compress_hyper_6_34_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_4 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_5 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk compress_hyper_6_32_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o2_6 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk compress_hyper_6_31_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2_7 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_8 (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o2_9 (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(61 rows) + +-- test join against max query +-- not ChunkAppend so no chunk exclusion +SET enable_hashjoin = FALSE; +:PREFIX +SELECT o1.time, + o2.* +FROM :TEST_TABLE o1 + INNER JOIN ( + SELECT max(time) AS max_time + FROM :TEST_TABLE) o2 ON o1.time = o2.max_time +WHERE o1.device_id = 1 +ORDER BY time; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Merge Join (actual rows=1 loops=1) + Merge Cond: (o1."time" = (max(_hyper_5_19_chunk."time"))) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=13674 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (actual rows=5038 loops=1) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + Index Cond: (device_id = 1) + -> Sort (actual rows=1 loops=1) + Sort Key: (max(_hyper_5_19_chunk."time")) + Sort Method: quicksort + -> Aggregate (actual rows=1 loops=1) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk (actual rows=10794 loops=1) + -> Seq Scan on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk (actual rows=3598 loops=1) + -> Seq Scan on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk (actual rows=15114 loops=1) + -> Seq Scan on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk (actual rows=5038 loops=1) + -> Seq Scan on compress_hyper_6_28_chunk (actual rows=6 loops=1) +(36 rows) + +RESET enable_hashjoin; +SET enable_seqscan TO false; +-- test JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with USING +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 USING (time) +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test NATURAL JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + NATURAL INNER JOIN :TEST_TABLE o2 +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +---------------------------------------------- + Limit (actual rows=0 loops=1) + -> Sort (actual rows=0 loops=1) + Sort Key: o1."time" + Sort Method: quicksort + -> Result (actual rows=0 loops=1) + One-Time Filter: false +(6 rows) + +-- test LEFT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + LEFT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test RIGHT JOIN on time column +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + RIGHT JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ON clause expression order switched +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o2.time = o1.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with equality condition in WHERE clause +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON TRUE +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with ORDER BY 2nd hypertable +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o2.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column and device_id +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id + AND o1.time = o2.time + ORDER BY o1.time + LIMIT 100; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Sort (actual rows=100 loops=1) + Sort Key: o1_1."time" + Sort Method: top-N heapsort + -> Hash Join (actual rows=68370 loops=1) + Hash Cond: ((o1_1.device_id = o2_1.device_id) AND (o1_1."time" = o2_1."time")) + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o1_2 (actual rows=10794 loops=1) + -> Index Scan using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o1_3 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_4 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o1_5 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o1_6 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_7 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o1_8 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o1_9 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk (actual rows=6 loops=1) + -> Hash (actual rows=68370 loops=1) + Buckets: 131072 Batches: 1 + -> Append (actual rows=68370 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2_1 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_2 (actual rows=10794 loops=1) + -> Index Scan using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk compress_hyper_6_35_chunk_1 (actual rows=12 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o2_3 (actual rows=3598 loops=1) + -> Index Scan using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk compress_hyper_6_34_chunk_1 (actual rows=4 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_4 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_5 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk compress_hyper_6_32_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o2_6 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk compress_hyper_6_31_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2_7 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=6 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_8 (actual rows=15114 loops=1) + -> Index Scan using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk compress_hyper_6_29_chunk_1 (actual rows=18 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o2_9 (actual rows=5038 loops=1) + -> Index Scan using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk compress_hyper_6_28_chunk_1 (actual rows=6 loops=1) +(46 rows) + +-- test JOIN on device_id +-- should not use ordered append for 2nd hypertable +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.device_id = o2.device_id +WHERE o1.device_id = 1 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=100 loops=1) + -> Nested Loop (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=1 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=1 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Append (actual rows=100 loops=1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk compress_hyper_6_30_chunk_1 (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o2_2 (never executed) + -> Index Scan using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk compress_hyper_6_36_chunk_1 (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o2_3 (never executed) + -> Index Scan using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk compress_hyper_6_33_chunk_1 (never executed) + Index Cond: (device_id = 1) +(24 rows) + +-- test JOIN on time column with implicit join +-- should use 2 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1, + :TEST_TABLE o2 +WHERE o1.time = o2.time + AND o1.device_id = 1 + AND o2.device_id = 2 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(26 rows) + +-- test JOIN on time column with 3 hypertables +-- should use 3 ChunkAppend +:PREFIX +SELECT o1.time +FROM :TEST_TABLE o1 + INNER JOIN :TEST_TABLE o2 ON o1.time = o2.time + INNER JOIN :TEST_TABLE o3 ON o1.time = o3.time +WHERE o1.device_id = 1 + AND o2.device_id = 2 + AND o3.device_id = 3 +ORDER BY o1.time +LIMIT 100; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o3."time" = o1."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o3 (actual rows=100 loops=1) + Order: o3."time" + -> Custom Scan (DecompressChunk) on _hyper_5_21_chunk o3_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_34_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_34_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_5_24_chunk o3_2 (never executed) + -> Index Scan Backward using compress_hyper_6_31_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_31_chunk (never executed) + Index Cond: (device_id = 3) + -> Custom Scan (DecompressChunk) on _hyper_5_27_chunk o3_3 (never executed) + -> Index Scan Backward using compress_hyper_6_28_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_28_chunk (never executed) + Index Cond: (device_id = 3) + -> Materialize (actual rows=100 loops=1) + -> Merge Join (actual rows=100 loops=1) + Merge Cond: (o1."time" = o2."time") + -> Custom Scan (ChunkAppend) on metrics_space_compressed o1 (actual rows=100 loops=1) + Order: o1."time" + -> Custom Scan (DecompressChunk) on _hyper_5_19_chunk o1_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_36_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_36_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_22_chunk o1_2 (never executed) + -> Index Scan Backward using compress_hyper_6_33_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_33_chunk (never executed) + Index Cond: (device_id = 1) + -> Custom Scan (DecompressChunk) on _hyper_5_25_chunk o1_3 (never executed) + -> Index Scan Backward using compress_hyper_6_30_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_30_chunk (never executed) + Index Cond: (device_id = 1) + -> Materialize (actual rows=100 loops=1) + -> Custom Scan (ChunkAppend) on metrics_space_compressed o2 (actual rows=100 loops=1) + Order: o2."time" + -> Custom Scan (DecompressChunk) on _hyper_5_20_chunk o2_1 (actual rows=100 loops=1) + -> Index Scan Backward using compress_hyper_6_35_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_35_chunk (actual rows=1 loops=1) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_23_chunk o2_2 (never executed) + -> Index Scan Backward using compress_hyper_6_32_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_32_chunk (never executed) + Index Cond: (device_id = 2) + -> Custom Scan (DecompressChunk) on _hyper_5_26_chunk o2_3 (never executed) + -> Index Scan Backward using compress_hyper_6_29_chunk__compressed_hypertable_6_device_id__t on compress_hyper_6_29_chunk (never executed) + Index Cond: (device_id = 2) +(40 rows) + +RESET enable_seqscan; +-- get results for all the queries +-- run queries on uncompressed hypertable and store result +\set PREFIX '' +\set PREFIX_VERBOSE '' +\set ECHO none diff --git a/tsl/test/shared/sql/.gitignore b/tsl/test/shared/sql/.gitignore index e81252862c9..9e13d27ec7e 100644 --- a/tsl/test/shared/sql/.gitignore +++ b/tsl/test/shared/sql/.gitignore @@ -1,2 +1,4 @@ /constify_timestamptz_op_interval-*.sql +/gapfill-*.sql /ordered_append-*.sql +/ordered_append_join-*.sql diff --git a/tsl/test/shared/sql/CMakeLists.txt b/tsl/test/shared/sql/CMakeLists.txt index f2d167b952e..ba2d691c4bb 100644 --- a/tsl/test/shared/sql/CMakeLists.txt +++ b/tsl/test/shared/sql/CMakeLists.txt @@ -3,13 +3,13 @@ set(TEST_FILES_SHARED decompress_placeholdervar.sql dist_gapfill.sql dist_insert.sql - gapfill.sql - ordered_append_join.sql ) set(TEST_TEMPLATES_SHARED constify_timestamptz_op_interval.sql.in + gapfill.sql.in ordered_append.sql.in + ordered_append_join.sql.in ) # Regression tests that vary with PostgreSQL version. Generated test diff --git a/tsl/test/shared/sql/constraint_exclusion_prepared.sql b/tsl/test/shared/sql/constraint_exclusion_prepared.sql index f49aa7f4495..00a7ba16603 100644 --- a/tsl/test/shared/sql/constraint_exclusion_prepared.sql +++ b/tsl/test/shared/sql/constraint_exclusion_prepared.sql @@ -16,6 +16,8 @@ SELECT format('\! diff -u --label "Uncompressed results" --label "Compressed res \set PREFIX_VERBOSE 'EXPLAIN (analyze, costs off, timing off, summary off, verbose)' set work_mem to '64MB'; +-- disable incremental sort here to make plans comparable to PG < 13 +SELECT CASE WHEN current_setting('server_version_num')::int/10000 >= 13 THEN set_config('enable_incremental_sort','off',false) ELSE 'off' END; set max_parallel_workers_per_gather to 0; \set TEST_TABLE 'metrics' diff --git a/tsl/test/shared/sql/gapfill.sql b/tsl/test/shared/sql/gapfill.sql.in similarity index 100% rename from tsl/test/shared/sql/gapfill.sql rename to tsl/test/shared/sql/gapfill.sql.in diff --git a/tsl/test/shared/sql/ordered_append_join.sql b/tsl/test/shared/sql/ordered_append_join.sql.in similarity index 100% rename from tsl/test/shared/sql/ordered_append_join.sql rename to tsl/test/shared/sql/ordered_append_join.sql.in