mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-17 11:03:36 +08:00
Ordered append for space partitioned hypertable would lead to an error when the ORDER BY clause was not a column reference on PG 9.6 and PG 10. This patch fixes ordered append for space partitioned hypertable and allows arbitary expressions to be used in the ORDER BY clause.
497 lines
15 KiB
SQL
497 lines
15 KiB
SQL
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
|
|
-- print chunks ordered by time to ensure ordering we want
|
|
SELECT
|
|
ht.table_name AS hypertable,
|
|
c.table_name AS chunk,
|
|
ds.range_start
|
|
FROM
|
|
_timescaledb_catalog.chunk c
|
|
INNER JOIN LATERAL(SELECT * FROM _timescaledb_catalog.chunk_constraint cc WHERE c.id = cc.chunk_id ORDER BY cc.dimension_slice_id LIMIT 1) cc ON true
|
|
INNER JOIN _timescaledb_catalog.dimension_slice ds ON ds.id=cc.dimension_slice_id
|
|
INNER JOIN _timescaledb_catalog.dimension d ON ds.dimension_id = d.id
|
|
INNER JOIN _timescaledb_catalog.hypertable ht ON d.hypertable_id = ht.id
|
|
ORDER BY ht.table_name, range_start, chunk;
|
|
|
|
-- test ASC for ordered chunks
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test DESC for ordered chunks
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test ASC for reverse ordered chunks
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append_reverse
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test DESC for reverse ordered chunks
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append_reverse
|
|
ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test query with ORDER BY column not in targetlist
|
|
:PREFIX SELECT
|
|
device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- ORDER BY may include other columns after time column
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time DESC, device_id LIMIT 1;
|
|
|
|
-- test RECORD in targetlist
|
|
:PREFIX SELECT
|
|
(time, device_id, value)
|
|
FROM ordered_append
|
|
ORDER BY time DESC, device_id LIMIT 1;
|
|
|
|
-- test sort column not in targetlist
|
|
:PREFIX SELECT
|
|
time_bucket('1h',time)
|
|
FROM ordered_append
|
|
ORDER BY time DESC LIMIT 1;
|
|
|
|
-- queries with ORDER BY non-time column shouldn't use ordered append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY device_id LIMIT 1;
|
|
|
|
-- time column must be primary sort order
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY device_id, time LIMIT 1;
|
|
|
|
-- queries without LIMIT should use ordered append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time ASC;
|
|
|
|
-- queries without ORDER BY shouldnt use ordered append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
LIMIT 1;
|
|
|
|
-- test interaction with constraint exclusion
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time > '2000-01-07'
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time > '2000-01-07'
|
|
ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test interaction with constraint aware append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time > now_s()
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time < now_s()
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test constraint exclusion
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time > now_s() AND time < '2000-01-10'
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ordered_append
|
|
WHERE time < now_s() AND time > '2000-01-07'
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- min/max queries
|
|
:PREFIX SELECT max(time) FROM ordered_append;
|
|
|
|
:PREFIX SELECT min(time) FROM ordered_append;
|
|
|
|
-- test first/last (doesn't use ordered append yet)
|
|
:PREFIX SELECT first(time, time) FROM ordered_append;
|
|
|
|
:PREFIX SELECT last(time, time) FROM ordered_append;
|
|
|
|
-- test query with time_bucket
|
|
:PREFIX SELECT
|
|
time_bucket('1d',time), device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test query with ORDER BY time_bucket
|
|
:PREFIX SELECT
|
|
time_bucket('1d',time), device_id, value
|
|
FROM ordered_append
|
|
ORDER BY 1 LIMIT 1;
|
|
|
|
-- test query with ORDER BY time_bucket
|
|
:PREFIX SELECT
|
|
time_bucket('1d',time), device_id, value
|
|
FROM ordered_append
|
|
ORDER BY time_bucket('1d',time) LIMIT 1;
|
|
|
|
-- test query with ORDER BY time_bucket, device_id
|
|
-- must not use ordered append
|
|
:PREFIX SELECT
|
|
time_bucket('1d',time), device_id, name
|
|
FROM dimension_last
|
|
ORDER BY time_bucket('1d',time), device_id LIMIT 1;
|
|
|
|
-- test query with ORDER BY date_trunc
|
|
:PREFIX SELECT
|
|
time_bucket('1d',time), device_id, value
|
|
FROM ordered_append
|
|
ORDER BY date_trunc('day', time) LIMIT 1;
|
|
|
|
-- test query with ORDER BY date_trunc
|
|
:PREFIX SELECT
|
|
date_trunc('day',time), device_id, value
|
|
FROM ordered_append
|
|
ORDER BY 1 LIMIT 1;
|
|
|
|
-- test query with ORDER BY date_trunc, device_id
|
|
-- must not use ordered append
|
|
:PREFIX SELECT
|
|
date_trunc('day',time), device_id, name
|
|
FROM dimension_last
|
|
ORDER BY 1,2 LIMIT 1;
|
|
|
|
-- test query with now() should result in ordered ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append WHERE time < now() + '1 month'
|
|
ORDER BY time DESC limit 1;
|
|
|
|
-- test CTE
|
|
:PREFIX WITH i AS (SELECT * FROM ordered_append WHERE time < now() ORDER BY time DESC limit 100)
|
|
SELECT * FROM i;
|
|
|
|
-- test LATERAL with ordered append in the outer query
|
|
:PREFIX SELECT * FROM ordered_append, LATERAL(SELECT * FROM (VALUES (1),(2)) v) l ORDER BY time DESC limit 2;
|
|
|
|
-- test LATERAL with ordered append in the lateral query
|
|
:PREFIX SELECT * FROM (VALUES (1),(2)) v, LATERAL(SELECT * FROM ordered_append ORDER BY time DESC limit 2) l;
|
|
|
|
-- test plan with best index is chosen
|
|
-- this should use device_id, time index
|
|
:PREFIX SELECT * FROM ordered_append WHERE device_id = 1 ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test plan with best index is chosen
|
|
-- this should use time index
|
|
:PREFIX SELECT * FROM ordered_append ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test with table with only dimension column
|
|
:PREFIX SELECT * FROM dimension_only ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test LEFT JOIN against hypertable
|
|
:PREFIX_NO_ANALYZE SELECT *
|
|
FROM dimension_last
|
|
LEFT JOIN dimension_only USING (time)
|
|
ORDER BY dimension_last.time DESC
|
|
LIMIT 2;
|
|
|
|
-- test INNER JOIN against non-hypertable
|
|
:PREFIX_NO_ANALYZE SELECT *
|
|
FROM dimension_last
|
|
INNER JOIN dimension_only USING (time)
|
|
ORDER BY dimension_last.time DESC
|
|
LIMIT 2;
|
|
|
|
-- test join against non-hypertable
|
|
:PREFIX SELECT *
|
|
FROM dimension_last
|
|
INNER JOIN devices USING(device_id)
|
|
ORDER BY dimension_last.time DESC
|
|
LIMIT 2;
|
|
|
|
-- test hypertable with index missing on one chunk
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ht_missing_indexes
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test hypertable with index missing on one chunk
|
|
-- and no data
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ht_missing_indexes
|
|
WHERE device_id = 2
|
|
ORDER BY time DESC LIMIT 1;
|
|
|
|
-- test hypertable with index missing on one chunk
|
|
-- and no data
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ht_missing_indexes
|
|
WHERE time > '2000-01-07'
|
|
ORDER BY time LIMIT 10;
|
|
|
|
-- test hypertable with dropped columns
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ht_dropped_columns
|
|
ORDER BY time ASC LIMIT 1;
|
|
|
|
-- test hypertable with dropped columns
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM ht_dropped_columns
|
|
WHERE device_id = 1
|
|
ORDER BY time DESC;
|
|
|
|
-- test hypertable with space partitioning
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
ORDER BY time;
|
|
|
|
-- test hypertable with space partitioning and exclusion in space
|
|
-- should remove 3 of 4 space partitions (2 chunks scanned)
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
WHERE device_id = 1
|
|
ORDER BY time;
|
|
|
|
-- test hypertable with space partitioning and exclusion in space
|
|
-- should remove 2 of 4 space partitions (2 + 2 chunks scanned)
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
WHERE device_id IN (1, 4)
|
|
ORDER BY time;
|
|
|
|
-- test hypertable with space partitioning and reverse order
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
ORDER BY time DESC;
|
|
|
|
-- test hypertable with space partitioning ORDER BY multiple columns
|
|
-- does not use ordered append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
ORDER BY time, device_id LIMIT 1;
|
|
|
|
-- test hypertable with space partitioning ORDER BY non-time column
|
|
-- does not use ordered append
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space
|
|
ORDER BY device_id, time LIMIT 1;
|
|
|
|
-- test hypertable with 2 space dimensions
|
|
:PREFIX SELECT
|
|
time, device_id, value
|
|
FROM space2
|
|
ORDER BY time DESC;
|
|
|
|
-- test hypertable with 3 space dimensions
|
|
:PREFIX SELECT
|
|
time
|
|
FROM space3
|
|
ORDER BY time DESC;
|
|
|
|
-- expressions in ORDER BY clause
|
|
:PREFIX SELECT
|
|
time_bucket('1h',time)
|
|
FROM space
|
|
ORDER BY 1 LIMIT 10;
|
|
|
|
:PREFIX SELECT
|
|
time_bucket('1h',time)
|
|
FROM space
|
|
ORDER BY 1 DESC LIMIT 10;
|
|
|
|
-- test LATERAL with correlated query
|
|
-- only last chunk should be executed
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-01'::timestamptz,'2000-01-03','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM ordered_append o
|
|
WHERE o.time >= g.time AND o.time < g.time + '1d'::interval ORDER BY time DESC LIMIT 1
|
|
) l ON true;
|
|
|
|
-- test LATERAL with correlated query
|
|
-- only 2nd chunk should be executed
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-10'::timestamptz,'2000-01-11','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM ordered_append o
|
|
WHERE o.time >= g.time AND o.time < g.time + '1d'::interval ORDER BY time LIMIT 1
|
|
) l ON true;
|
|
|
|
-- test startup and runtime exclusion together
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-01'::timestamptz,'2000-01-03','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM ordered_append 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;
|
|
|
|
-- test startup and runtime exclusion together
|
|
-- all chunks should be filtered
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-01'::timestamptz,'2000-01-03','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM ordered_append 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;
|
|
|
|
-- test CTE
|
|
-- no chunk exclusion for CTE because cte query is not pulled up
|
|
:PREFIX WITH cte AS (SELECT * FROM ordered_append ORDER BY time)
|
|
SELECT * FROM cte WHERE time < '2000-02-01'::timestamptz;
|
|
|
|
-- test JOIN
|
|
-- no exclusion on joined table because quals are not propagated yet
|
|
:PREFIX SELECT *
|
|
FROM ordered_append o1
|
|
INNER JOIN ordered_append o2 ON o1.time = o2.time
|
|
WHERE o1.time < '2000-02-01'
|
|
ORDER BY o1.time;
|
|
|
|
-- test JOIN
|
|
-- last chunk of o2 should not be executed
|
|
:PREFIX SELECT *
|
|
FROM ordered_append o1
|
|
INNER JOIN (SELECT * FROM ordered_append o2 ORDER BY time) o2 ON o1.time = o2.time
|
|
WHERE o1.time < '2000-01-08'
|
|
ORDER BY o1.time;
|
|
|
|
-- test subquery
|
|
-- not ChunkAppend so no chunk exclusion
|
|
:PREFIX SELECT *
|
|
FROM ordered_append WHERE time = (SELECT max(time) FROM ordered_append) ORDER BY time;
|
|
|
|
-- test join against max query
|
|
-- not ChunkAppend so no chunk exclusion
|
|
:PREFIX SELECT *
|
|
FROM ordered_append o1 INNER JOIN (SELECT max(time) AS max_time FROM ordered_append) o2 ON o1.time = o2.max_time ORDER BY time;
|
|
|
|
-- test ordered append with limit expression
|
|
:PREFIX SELECT *
|
|
FROM ordered_append ORDER BY time LIMIT (SELECT length('four'));
|
|
|
|
-- test with ordered guc disabled
|
|
SET timescaledb.enable_ordered_append TO off;
|
|
:PREFIX SELECT *
|
|
FROM ordered_append ORDER BY time LIMIT 3;
|
|
|
|
RESET timescaledb.enable_ordered_append;
|
|
:PREFIX SELECT *
|
|
FROM ordered_append ORDER BY time LIMIT 3;
|
|
|
|
-- test with chunk append disabled
|
|
SET timescaledb.enable_chunk_append TO off;
|
|
:PREFIX SELECT *
|
|
FROM ordered_append ORDER BY time LIMIT 3;
|
|
|
|
RESET timescaledb.enable_chunk_append;
|
|
:PREFIX SELECT *
|
|
FROM ordered_append ORDER BY time LIMIT 3;
|
|
|
|
-- test space partitioning with startup exclusion
|
|
:PREFIX SELECT *
|
|
FROM space WHERE time < now() ORDER BY time;
|
|
|
|
-- test runtime exclusion together with space partitioning
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-01'::timestamptz,'2000-01-03','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM space o
|
|
WHERE o.time >= g.time AND o.time < g.time + '1d'::interval ORDER BY time DESC LIMIT 1
|
|
) l ON true;
|
|
|
|
-- test startup and runtime exclusion together with space partitioning
|
|
:PREFIX SELECT *
|
|
FROM generate_series('2000-01-01'::timestamptz,'2000-01-03','1d') AS g(time)
|
|
LEFT OUTER JOIN LATERAL(
|
|
SELECT * FROM space 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;
|
|
|
|
-- test JOIN on time column
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o1.time = o2.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with USING
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 USING(time) ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test NATURAL JOIN on time column
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 NATURAL INNER JOIN ordered_append o2 ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test LEFT JOIN on time column
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 LEFT JOIN ordered_append o2 ON o1.time=o2.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test RIGHT JOIN on time column
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 RIGHT JOIN ordered_append o2 ON o1.time=o2.time ORDER BY o2.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with ON clause expression order switched
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o2.time = o1.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with equality condition in WHERE clause
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON true WHERE o1.time = o2.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with ORDER BY 2nd hypertable
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o1.time = o2.time ORDER BY o2.time LIMIT 100;
|
|
|
|
-- test JOIN on time column and device_id
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o1.device_id = o2.device_id AND o1.time = o2.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on device_id
|
|
-- should not use ordered append for 2nd hypertable
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o1.device_id = o2.device_id ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with implicit join
|
|
-- should use 2 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1, ordered_append o2 WHERE o1.time = o2.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test JOIN on time column with 3 hypertables
|
|
-- should use 3 ChunkAppend
|
|
:PREFIX SELECT * FROM ordered_append o1 INNER JOIN ordered_append o2 ON o1.time = o2.time INNER JOIN ordered_append o3 ON o1.time = o3.time ORDER BY o1.time LIMIT 100;
|
|
|
|
-- test with space partitioning
|
|
:PREFIX SELECT * FROM space s1 INNER JOIN space s2 ON s1.time = s2.time ORDER BY s1.time LIMIT 100;
|
|
|
|
-- test COLLATION
|
|
-- cant be tested in our ci because alpine doesnt support locales
|
|
-- :PREFIX SELECT * FROM sortopt_test ORDER BY time, device COLLATE "en_US.utf8";
|
|
|
|
-- test NULLS FIRST
|
|
:PREFIX SELECT * FROM sortopt_test ORDER BY time, device NULLS FIRST;
|
|
|
|
-- test NULLS LAST
|
|
:PREFIX SELECT * FROM sortopt_test ORDER BY time, device DESC NULLS LAST;
|