From ed11a162ec89b7f5b73b80787515a01e8d4e8eee Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 6 Mar 2019 19:26:00 +0100 Subject: [PATCH] Adjust ordered append plan cost Ordered append used to only consider startup cost of the first child. This patch changes the cost for ordered append to also include the total cost of the first child --- CHANGELOG.md | 2 + src/plan_ordered_append.c | 14 ++-- test/expected/plan_ordered_append-10.out | 68 +++++++++++++------ test/expected/plan_ordered_append-11.out | 44 +++++++++--- test/expected/plan_ordered_append-9.6.out | 65 +++++++++++++----- test/sql/include/plan_ordered_append_load.sql | 1 + .../sql/include/plan_ordered_append_query.sql | 9 +++ 7 files changed, 148 insertions(+), 55 deletions(-) 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; + +