diff --git a/CHANGELOG.md b/CHANGELOG.md index 69be22615..89dcb2795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ accidentally triggering the load of a previous DB version.** * PR #1067 Add treat_null_as_missing option to locf **Bugfixes** +* PR #1097 Adjust ordered append plan cost * PR #1079 Stop background worker on ALTER DATABASE SET TABLESPACE and CREATE DATABASE WITH TEMPLATE * PR #1088 Fix ON CONFLICT when using prepared statements and functions * PR #1089 Fix compatibility with extensions that define planner_hook @@ -26,6 +27,7 @@ accidentally triggering the load of a previous DB version.** * @naquad for reporting a segfault when using ON conflict in stored procedures * @aaronkaplan for reporting an issue with ALTER DATABASE SET TABLESPACE * @quetz for reporting an issue with CREATE DATABASE WITH TEMPLATE +* @nbouscal for reporting an issue with ordered append resulting in bad plans ## 1.2.1 (2019-02-11) diff --git a/src/plan_ordered_append.c b/src/plan_ordered_append.c index 3c0a9d1c2..2201d37bb 100644 --- a/src/plan_ordered_append.c +++ b/src/plan_ordered_append.c @@ -132,17 +132,18 @@ ts_ordered_append_path_create(PlannerInfo *root, RelOptInfo *rel, Hypertable *ht } /* - * we set subpaths as NIL initially to skip PostgreSQL cost calculation - * for children + * we use normal postgresql append cost calculation, which means + * the cost estimate is rather pessimistic */ #if PG96 - append = create_append_path(rel, NIL, PATH_REQ_OUTER(&merge->path), 0); + append = create_append_path(rel, sorted, PATH_REQ_OUTER(&merge->path), 0); #elif PG10 - append = create_append_path(rel, NIL, PATH_REQ_OUTER(&merge->path), 0, merge->partitioned_rels); + append = + create_append_path(rel, sorted, PATH_REQ_OUTER(&merge->path), 0, merge->partitioned_rels); #else append = create_append_path(root, rel, - NIL, + sorted, NULL, PATH_REQ_OUTER(&merge->path), 0, @@ -151,9 +152,6 @@ ts_ordered_append_path_create(PlannerInfo *root, RelOptInfo *rel, Hypertable *ht root->limit_tuples); #endif - append->subpaths = sorted; - if (list_length(sorted) > 0) - append->path.startup_cost = ((Path *) linitial(sorted))->startup_cost; append->path.pathkeys = merge->path.pathkeys; append->path.parallel_aware = false; append->path.parallel_safe = parallel_safe; diff --git a/test/expected/plan_ordered_append-10.out b/test/expected/plan_ordered_append-10.out index 3ce044031..8edbaa000 100644 --- a/test/expected/plan_ordered_append-10.out +++ b/test/expected/plan_ordered_append-10.out @@ -37,6 +37,7 @@ SELECT create_hypertable('ordered_append','time'); (1 row) CREATE index on ordered_append(time DESC,device_id); +CREATE index on ordered_append(device_id,time DESC); INSERT INTO ordered_append VALUES('2000-01-01',1,1.0); INSERT INTO ordered_append VALUES('2000-01-08',1,2.0); INSERT INTO ordered_append VALUES('2000-01-15',1,3.0); @@ -166,17 +167,15 @@ ORDER BY time DESC, device_id LIMIT 1; time, device_id, value FROM ordered_append ORDER BY device_id LIMIT 1; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- Limit (actual rows=1 loops=1) - -> Sort (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) Sort Key: _hyper_1_1_chunk.device_id - Sort Method: top-N heapsort Memory: 25kB - -> Append (actual rows=3 loops=1) - -> Seq Scan on _hyper_1_1_chunk (actual rows=1 loops=1) - -> Seq Scan on _hyper_1_2_chunk (actual rows=1 loops=1) - -> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1) -(8 rows) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_device_id_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) +(6 rows) -- time column must be primary sort order :PREFIX SELECT @@ -497,19 +496,48 @@ SELECT * FROM i; -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk (actual rows=1 loops=1) -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk (never executed) -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) - -> Values Scan on "*VALUES*" (actual rows=2 loops=1) -(7 rows) + -> Materialize (actual rows=2 loops=1) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) +(8 rows) -- 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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------- Nested Loop (actual rows=4 loops=1) - -> Limit (actual rows=2 loops=1) - -> Append (actual rows=2 loops=1) - -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk (actual rows=1 loops=1) - -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk (actual rows=1 loops=1) - -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) - -> Values Scan on "*VALUES*" (actual rows=2 loops=2) -(7 rows) + -> Values Scan on "*VALUES*" (actual rows=2 loops=1) + -> Materialize (actual rows=2 loops=2) + -> Limit (actual rows=2 loops=1) + -> Append (actual rows=2 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) +(8 rows) + +-- 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_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_ordered_append_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) +(8 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX SELECT * FROM ordered_append ORDER BY time DESC LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) +(5 rows) diff --git a/test/expected/plan_ordered_append-11.out b/test/expected/plan_ordered_append-11.out index bcdc8ae3b..8edbaa000 100644 --- a/test/expected/plan_ordered_append-11.out +++ b/test/expected/plan_ordered_append-11.out @@ -37,6 +37,7 @@ SELECT create_hypertable('ordered_append','time'); (1 row) CREATE index on ordered_append(time DESC,device_id); +CREATE index on ordered_append(device_id,time DESC); INSERT INTO ordered_append VALUES('2000-01-01',1,1.0); INSERT INTO ordered_append VALUES('2000-01-08',1,2.0); INSERT INTO ordered_append VALUES('2000-01-15',1,3.0); @@ -166,17 +167,15 @@ ORDER BY time DESC, device_id LIMIT 1; time, device_id, value FROM ordered_append ORDER BY device_id LIMIT 1; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- Limit (actual rows=1 loops=1) - -> Sort (actual rows=1 loops=1) + -> Merge Append (actual rows=1 loops=1) Sort Key: _hyper_1_1_chunk.device_id - Sort Method: top-N heapsort Memory: 25kB - -> Append (actual rows=3 loops=1) - -> Seq Scan on _hyper_1_1_chunk (actual rows=1 loops=1) - -> Seq Scan on _hyper_1_2_chunk (actual rows=1 loops=1) - -> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1) -(8 rows) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_device_id_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_device_id_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1) +(6 rows) -- time column must be primary sort order :PREFIX SELECT @@ -515,3 +514,30 @@ SELECT * FROM i; -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) (8 rows) +-- 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_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_ordered_append_device_id_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: (device_id = 1) +(8 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX SELECT * FROM ordered_append ORDER BY time DESC LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Limit (actual rows=1 loops=1) + -> Append (actual rows=1 loops=1) + -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk (actual rows=1 loops=1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk (never executed) +(5 rows) + diff --git a/test/expected/plan_ordered_append-9.6.out b/test/expected/plan_ordered_append-9.6.out index f25e3e71b..b15ddb45d 100644 --- a/test/expected/plan_ordered_append-9.6.out +++ b/test/expected/plan_ordered_append-9.6.out @@ -37,6 +37,7 @@ SELECT create_hypertable('ordered_append','time'); (1 row) CREATE index on ordered_append(time DESC,device_id); +CREATE index on ordered_append(device_id,time DESC); INSERT INTO ordered_append VALUES('2000-01-01',1,1.0); INSERT INTO ordered_append VALUES('2000-01-08',1,2.0); INSERT INTO ordered_append VALUES('2000-01-15',1,3.0); @@ -166,16 +167,15 @@ ORDER BY time DESC, device_id LIMIT 1; time, device_id, value FROM ordered_append ORDER BY device_id LIMIT 1; - QUERY PLAN ------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------- Limit - -> Sort + -> Merge Append Sort Key: _hyper_1_1_chunk.device_id - -> Append - -> Seq Scan on _hyper_1_1_chunk - -> Seq Scan on _hyper_1_2_chunk - -> Seq Scan on _hyper_1_3_chunk -(7 rows) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk + -> Index Scan using _hyper_1_2_chunk_ordered_append_device_id_time_idx on _hyper_1_2_chunk + -> Index Scan using _hyper_1_3_chunk_ordered_append_device_id_time_idx on _hyper_1_3_chunk +(6 rows) -- time column must be primary sort order :PREFIX SELECT @@ -483,19 +483,48 @@ SELECT * FROM i; -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk - -> Values Scan on "*VALUES*" -(7 rows) + -> Materialize + -> Values Scan on "*VALUES*" +(8 rows) -- 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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- Nested Loop - -> Limit - -> Append - -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk - -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk - -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk -> Values Scan on "*VALUES*" -(7 rows) + -> Materialize + -> Limit + -> Append + -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk + -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk + -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk +(8 rows) + +-- 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Limit + -> Append + -> Index Scan using _hyper_1_3_chunk_ordered_append_device_id_time_idx on _hyper_1_3_chunk + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_2_chunk_ordered_append_device_id_time_idx on _hyper_1_2_chunk + Index Cond: (device_id = 1) + -> Index Scan using _hyper_1_1_chunk_ordered_append_device_id_time_idx on _hyper_1_1_chunk + Index Cond: (device_id = 1) +(8 rows) + +-- test plan with best index is chosen +-- this should use time index +:PREFIX SELECT * FROM ordered_append ORDER BY time DESC LIMIT 1; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Limit + -> Append + -> Index Scan using _hyper_1_3_chunk_ordered_append_time_device_id_idx on _hyper_1_3_chunk + -> Index Scan using _hyper_1_2_chunk_ordered_append_time_device_id_idx on _hyper_1_2_chunk + -> Index Scan using _hyper_1_1_chunk_ordered_append_time_device_id_idx on _hyper_1_1_chunk +(5 rows) diff --git a/test/sql/include/plan_ordered_append_load.sql b/test/sql/include/plan_ordered_append_load.sql index a8f4e07e2..bc04da92d 100644 --- a/test/sql/include/plan_ordered_append_load.sql +++ b/test/sql/include/plan_ordered_append_load.sql @@ -16,6 +16,7 @@ $BODY$; CREATE TABLE ordered_append(time timestamptz NOT NULL, device_id INT, value float); SELECT create_hypertable('ordered_append','time'); CREATE index on ordered_append(time DESC,device_id); +CREATE index on ordered_append(device_id,time DESC); INSERT INTO ordered_append VALUES('2000-01-01',1,1.0); INSERT INTO ordered_append VALUES('2000-01-08',1,2.0); diff --git a/test/sql/include/plan_ordered_append_query.sql b/test/sql/include/plan_ordered_append_query.sql index 03c1a1cc2..6fff07ba8 100644 --- a/test/sql/include/plan_ordered_append_query.sql +++ b/test/sql/include/plan_ordered_append_query.sql @@ -156,3 +156,12 @@ SELECT * FROM i; -- 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; + +