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
This commit is contained in:
Sven Klemm 2019-03-06 19:26:00 +01:00 committed by Sven Klemm
parent 300b05daf0
commit ed11a162ec
7 changed files with 148 additions and 55 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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;