From 7b3dce29bd58476e7078445368d0fdedf415d501 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Thu, 18 Jun 2026 21:46:01 +0200 Subject: [PATCH 1/8] Don't loop over all tables in per-table expansion hook This looks like leftover of the old approach where we expanded all hypertables at the beginning of the query. The current code expands all hypertables when the "set pathlist" hook is called for the first hypertable. This doesn't seem to lead to any problems, but this control flow looks unexpected. Just let the Postgres enumerate all tables in query normally, and in the hook, expand only the hypertable it is called for. --- src/planner/planner.c | 89 ++++++++++++------------------------------- 1 file changed, 24 insertions(+), 65 deletions(-) diff --git a/src/planner/planner.c b/src/planner/planner.c index 873cb9ae1ed..019b641d511 100644 --- a/src/planner/planner.c +++ b/src/planner/planner.c @@ -1168,69 +1168,27 @@ rte_should_expand(const RangeTblEntry *rte) static void expand_hypertables(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { - bool set_pathlist_for_current_rel = false; + Hypertable *ht; double total_pages; - bool reenabled_inheritance = false; - for (int i = 1; i < root->simple_rel_array_size; i++) - { - RangeTblEntry *in_rte = root->simple_rte_array[i]; + Assert(rte_should_expand(rte)); + Assert(rti == rel->relid); -#if PG18_GE - /* RTE could be removed due to self-join - * elimination optimization. - * - * https://github.com/postgres/postgres/commit/5f6f95 - */ - if (!in_rte) - { - continue; - } -#endif - - if (rte_should_expand(in_rte) && root->simple_rel_array[i]) - { - RelOptInfo *in_rel = root->simple_rel_array[i]; - Hypertable *ht = ts_planner_get_hypertable(in_rte->relid, CACHE_FLAG_NOCREATE); - - Assert(ht != NULL && in_rel != NULL); - ts_plan_expand_hypertable_chunks(ht, root, in_rel, in_rte->ctename != TS_FK_EXPAND); - - in_rte->inh = true; - reenabled_inheritance = true; - /* Redo set_rel_consider_parallel, as results of the call may no longer be valid - * here (due to adding more tables to the set of tables under consideration here). - * This is especially true if dealing with foreign data wrappers. */ - - /* - * An entry of reloptkind RELOPT_OTHER_MEMBER_REL might still - * be a hypertable here if it was pulled up from a subquery - * as happens with UNION ALL for example. - */ - if (in_rel->reloptkind == RELOPT_BASEREL || - in_rel->reloptkind == RELOPT_OTHER_MEMBER_REL) - { - Assert(in_rte->relkind == RELKIND_RELATION); - ts_set_rel_size(root, in_rel, i, in_rte); - } + ht = ts_planner_get_hypertable(rte->relid, CACHE_FLAG_NOCREATE); + Assert(ht != NULL); + ts_plan_expand_hypertable_chunks(ht, root, rel, rte->ctename != TS_FK_EXPAND); - /* if we're activating inheritance during a hypertable's pathlist - * creation then we're past the point at which postgres will add - * paths for the children, and we have to do it ourselves. We delay - * the actual setting of the pathlists until after this loop, - * because set_append_rel_pathlist will eventually call this hook again. - */ - if (in_rte == rte) - { - Assert(rti == (Index) i); - set_pathlist_for_current_rel = true; - } - } - } + rte->inh = true; - if (!reenabled_inheritance) + /* + * An entry of reloptkind RELOPT_OTHER_MEMBER_REL might still + * be a hypertable here if it was pulled up from a subquery + * as happens with UNION ALL for example. + */ + if (rel->reloptkind == RELOPT_BASEREL || rel->reloptkind == RELOPT_OTHER_MEMBER_REL) { - return; + Assert(rte->relkind == RELKIND_RELATION); + ts_set_rel_size(root, rel, rti, rte); } total_pages = 0; @@ -1257,13 +1215,14 @@ expand_hypertables(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry } root->total_table_pages = total_pages; - if (set_pathlist_for_current_rel) - { - rel->pathlist = NIL; - rel->partial_pathlist = NIL; - - ts_set_append_rel_pathlist(root, rel, rti, rte); - } + /* + * We are past the point at which postgres will add paths for the children, + * so we have to do it ourselves. set_append_rel_pathlist will eventually + * call this hook again for each child chunk. + */ + rel->pathlist = NIL; + rel->partial_pathlist = NIL; + ts_set_append_rel_pathlist(root, rel, rti, rte); } static void @@ -1447,7 +1406,7 @@ timescaledb_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, Rang reltype = ts_classify_relation(root, rel, &ht); /* Check for unexpanded hypertable */ - if (!rte->inh && ts_rte_is_marked_for_expansion(rte)) + if (rte_should_expand(rte)) { expand_hypertables(root, rel, rti, rte); } From ac879bb8e122c8c52e1e0af6fadb662c8fb5cf8b Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:57:23 +0200 Subject: [PATCH 2/8] reference REL_18_4-67-gb1ab4bc52a1 append plan_expand_hypertable --- test/expected/append-18.out | 2 +- test/expected/plan_expand_hypertable-18.out | 102 +++++++++----------- 2 files changed, 45 insertions(+), 59 deletions(-) diff --git a/test/expected/append-18.out b/test/expected/append-18.out index f8fa120309c..5584a964442 100644 --- a/test/expected/append-18.out +++ b/test/expected/append-18.out @@ -1746,7 +1746,7 @@ DROP INDEX :INDEX_NAME; Limit (actual rows=3.00 loops=1) -> Merge Join (actual rows=3.00 loops=1) Merge Cond: (m2."time" = m1."time") - Join Filter: (m1.device_id = m2.device_id) + Join Filter: (m2.device_id = m1.device_id) Rows Removed by Join Filter: 4 -> Custom Scan (ChunkAppend) on join_limit m2 (actual rows=3.00 loops=1) Order: m2."time", m2.device_id diff --git a/test/expected/plan_expand_hypertable-18.out b/test/expected/plan_expand_hypertable-18.out index 7ecb93f60df..fd05505c8ff 100644 --- a/test/expected/plan_expand_hypertable-18.out +++ b/test/expected/plan_expand_hypertable-18.out @@ -3013,52 +3013,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) :PREFIX @@ -3074,52 +3067,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) \set ECHO errors From b6f50d3ffff5807ab351f83ca068331675ed3d72 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:40:36 +0200 Subject: [PATCH 3/8] reference REL_16_8 append plan_expand_hypertable --- test/expected/append-16.out | 2 +- test/expected/plan_expand_hypertable-16.out | 102 +++++++++----------- 2 files changed, 45 insertions(+), 59 deletions(-) diff --git a/test/expected/append-16.out b/test/expected/append-16.out index cfcc56f18ab..43a9cfe4acb 100644 --- a/test/expected/append-16.out +++ b/test/expected/append-16.out @@ -1748,7 +1748,7 @@ DROP INDEX :INDEX_NAME; Limit (actual rows=3.00 loops=1) -> Merge Join (actual rows=3.00 loops=1) Merge Cond: (m2."time" = m1."time") - Join Filter: (m1.device_id = m2.device_id) + Join Filter: (m2.device_id = m1.device_id) Rows Removed by Join Filter: 4 -> Custom Scan (ChunkAppend) on join_limit m2 (actual rows=3.00 loops=1) Order: m2."time", m2.device_id diff --git a/test/expected/plan_expand_hypertable-16.out b/test/expected/plan_expand_hypertable-16.out index dfcdaf69ac1..92662154219 100644 --- a/test/expected/plan_expand_hypertable-16.out +++ b/test/expected/plan_expand_hypertable-16.out @@ -3013,52 +3013,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) :PREFIX @@ -3074,52 +3067,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) \set ECHO errors From b905a101308cda4ec6e6335f5262c3076b76f641 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:05:55 +0200 Subject: [PATCH 4/8] reference REL_17_6 append plan_expand_hypertable --- test/expected/append-17.out | 2 +- test/expected/plan_expand_hypertable-17.out | 102 +++++++++----------- 2 files changed, 45 insertions(+), 59 deletions(-) diff --git a/test/expected/append-17.out b/test/expected/append-17.out index c40d9e1be91..42da8954e60 100644 --- a/test/expected/append-17.out +++ b/test/expected/append-17.out @@ -1743,7 +1743,7 @@ DROP INDEX :INDEX_NAME; Limit (actual rows=3.00 loops=1) -> Merge Join (actual rows=3.00 loops=1) Merge Cond: (m2."time" = m1."time") - Join Filter: (m1.device_id = m2.device_id) + Join Filter: (m2.device_id = m1.device_id) Rows Removed by Join Filter: 4 -> Custom Scan (ChunkAppend) on join_limit m2 (actual rows=3.00 loops=1) Order: m2."time", m2.device_id diff --git a/test/expected/plan_expand_hypertable-17.out b/test/expected/plan_expand_hypertable-17.out index 7ecb93f60df..fd05505c8ff 100644 --- a/test/expected/plan_expand_hypertable-17.out +++ b/test/expected/plan_expand_hypertable-17.out @@ -3013,52 +3013,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) :PREFIX @@ -3074,52 +3067,45 @@ SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timesta Hash Cond: (o2_m1."time" = o1_m1."time") -> Nested Loop -> Append - -> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 - Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_160_chunk o2_m1_1 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_169_chunk o2_m2_2 + -> Seq Scan on _hyper_6_161_chunk o2_m1_2 Filter: (device_id = 2) - -> Seq Scan on _hyper_7_170_chunk o2_m2_3 + -> Seq Scan on _hyper_6_162_chunk o2_m1_3 Filter: (device_id = 2) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1_1 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_163_chunk o2_m1_4 Filter: (device_id = 2) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_2 - Index Cond: ("time" = o2_m2."time") + -> Seq Scan on _hyper_6_164_chunk o2_m1_5 Filter: (device_id = 2) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_3 - Index Cond: ("time" = o2_m2."time") + -> Append + -> Index Scan using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_1 + Index Cond: (("time" = o2_m1."time") AND ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 2) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_4 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_5 - Index Cond: ("time" = o2_m2."time") + -> Index Scan using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_3 + Index Cond: ("time" = o2_m1."time") Filter: (device_id = 2) -> Hash -> Nested Loop -> Append - -> Seq Scan on _hyper_7_165_chunk o1_m2_1 + -> Seq Scan on _hyper_6_160_chunk o1_m1_1 Filter: (device_id = 1) - -> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 - Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone) + -> Seq Scan on _hyper_6_161_chunk o1_m1_2 Filter: (device_id = 1) - -> Append - -> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1_1 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_162_chunk o1_m1_3 Filter: (device_id = 1) - -> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_2 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_163_chunk o1_m1_4 Filter: (device_id = 1) - -> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_3 - Index Cond: ("time" = o1_m2."time") + -> Seq Scan on _hyper_6_164_chunk o1_m1_5 Filter: (device_id = 1) - -> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_4 - Index Cond: ("time" = o1_m2."time") + -> Append + -> Index Scan using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1 + Index Cond: ("time" = o1_m1."time") Filter: (device_id = 1) - -> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_5 - Index Cond: ("time" = o1_m2."time") + -> Index Scan using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk o1_m2_2 + Index Cond: (("time" = o1_m1."time") AND ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: (device_id = 1) \set ECHO errors From 8c0dd3b1df39a03d595440ddd78c5dc62bf74a26 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:44:33 +0200 Subject: [PATCH 5/8] cleanup? --- src/planner/planner.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/planner/planner.c b/src/planner/planner.c index 019b641d511..e0e01a24300 100644 --- a/src/planner/planner.c +++ b/src/planner/planner.c @@ -1166,7 +1166,7 @@ rte_should_expand(const RangeTblEntry *rte) } static void -expand_hypertables(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) +expand_hypertable(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { Hypertable *ht; double total_pages; @@ -1408,18 +1408,7 @@ timescaledb_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, Rang /* Check for unexpanded hypertable */ if (rte_should_expand(rte)) { - expand_hypertables(root, rel, rti, rte); - } - - if (ts_guc_enable_optimizations) - { - ts_planner_constraint_cleanup(root, rel); - } - - /* Call other extensions. Do it after table expansion. */ - if (prev_set_rel_pathlist_hook != NULL) - { - (*prev_set_rel_pathlist_hook)(root, rel, rti, rte); + expand_hypertable(root, rel, rti, rte); } switch (reltype) @@ -1469,6 +1458,17 @@ timescaledb_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, Rang apply_optimizations(root, reltype, rel, rte, ht); break; } + + if (ts_guc_enable_optimizations) + { + ts_planner_constraint_cleanup(root, rel); + } + + /* Call other extensions. Do it after table expansion. */ + if (prev_set_rel_pathlist_hook != NULL) + { + (*prev_set_rel_pathlist_hook)(root, rel, rti, rte); + } } /* This hook is meant to editorialize about the information the planner gets From dbad2e1c89119056bbb9c34eb91fdc6a80b08b65 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:17:40 +0200 Subject: [PATCH 6/8] more cleanup? --- src/import/allpaths.c | 7 ++++++ src/planner/planner.c | 56 ++++++++++++------------------------------- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/import/allpaths.c b/src/import/allpaths.c index f3a16b15588..479af6acd77 100644 --- a/src/import/allpaths.c +++ b/src/import/allpaths.c @@ -223,6 +223,13 @@ ts_set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *parent_rel, Index pare * startup costs on a hypertable */ if (child_rel->consider_startup) parent_rel->consider_startup = true; + + /* + * This calculation is done by make_one_rel, but it happens before our + * hypertable expansion, so we have to account for newly expanded chunks + * separately. + */ + root->total_table_pages += child_rel->pages; } /* Add paths to the append relation. */ diff --git a/src/planner/planner.c b/src/planner/planner.c index ce260a30de3..53f95c8b5d6 100644 --- a/src/planner/planner.c +++ b/src/planner/planner.c @@ -1206,30 +1206,6 @@ expand_hypertable(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry * ts_set_rel_size(root, rel, rti, rte); } - total_pages = 0; - for (int i = 1; i < root->simple_rel_array_size; i++) - { - RelOptInfo *brel = root->simple_rel_array[i]; - - if (brel == NULL) - { - continue; - } - - Assert(brel->relid == (Index) i); /* sanity check on array */ - - if (IS_DUMMY_REL(brel)) - { - continue; - } - - if (IS_SIMPLE_REL(brel)) - { - total_pages += (double) brel->pages; - } - } - root->total_table_pages = total_pages; - /* * We are past the point at which postgres will add paths for the children, * so we have to do it ourselves. set_append_rel_pathlist will eventually @@ -1420,12 +1396,6 @@ timescaledb_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, Rang reltype = ts_classify_relation(root, rel, &ht); - /* Check for unexpanded hypertable */ - if (rte_should_expand(rte)) - { - expand_hypertable(root, rel, rti, rte); - } - switch (reltype) { case TS_REL_HYPERTABLE_CHILD: @@ -1455,17 +1425,21 @@ timescaledb_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, Rang case TS_REL_HYPERTABLE: if (!rte->inh) { - /* - * This happens with SELECT FROM ONLY hypertable or with an - * empty hypertable. Mark it as dummy, otherwise we'll get a - * scan on hypertable relation itself. It's always empty, so - * this scan is useless and looks misleading. - */ - mark_dummy_rel(rel); - } - else - { - apply_optimizations(root, reltype, rel, rte, ht); + if (ts_rte_is_marked_for_expansion(rte)) + { + expand_hypertable(root, rel, rti, rte); + apply_optimizations(root, reltype, rel, rte, ht); + } + else + { + /* + * This happens with SELECT FROM ONLY hypertable or with an + * empty hypertable. Mark it as dummy, otherwise we'll get a + * scan on hypertable relation itself. It's always empty, so + * this scan is useless and looks misleading. + */ + mark_dummy_rel(rel); + } } break; From baf2d7cb32dbdf74e4d9e82b747b5b1538b41dfc Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:44:30 +0200 Subject: [PATCH 7/8] oops --- src/planner/planner.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/planner/planner.c b/src/planner/planner.c index 53f95c8b5d6..29a91d5f814 100644 --- a/src/planner/planner.c +++ b/src/planner/planner.c @@ -1183,13 +1183,10 @@ rte_should_expand(const RangeTblEntry *rte) static void expand_hypertable(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { - Hypertable *ht; - double total_pages; - Assert(rte_should_expand(rte)); Assert(rti == rel->relid); - ht = ts_planner_get_hypertable(rte->relid, CACHE_FLAG_NOCREATE); + Hypertable *ht = ts_planner_get_hypertable(rte->relid, CACHE_FLAG_NOCREATE); Assert(ht != NULL); ts_plan_expand_hypertable_chunks(ht, root, rel, rte->ctename != TS_FK_EXPAND); From 956d2f252219deaa9ff561cfeda9a376885944a6 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:59:55 +0200 Subject: [PATCH 8/8] wrong place? --- src/import/allpaths.c | 7 ------- src/planner/expand_hypertable.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/import/allpaths.c b/src/import/allpaths.c index 479af6acd77..f3a16b15588 100644 --- a/src/import/allpaths.c +++ b/src/import/allpaths.c @@ -223,13 +223,6 @@ ts_set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *parent_rel, Index pare * startup costs on a hypertable */ if (child_rel->consider_startup) parent_rel->consider_startup = true; - - /* - * This calculation is done by make_one_rel, but it happens before our - * hypertable expansion, so we have to account for newly expanded chunks - * separately. - */ - root->total_table_pages += child_rel->pages; } /* Add paths to the append relation. */ diff --git a/src/planner/expand_hypertable.c b/src/planner/expand_hypertable.c index c2c933ca8c8..968c5734672 100644 --- a/src/planner/expand_hypertable.c +++ b/src/planner/expand_hypertable.c @@ -1518,6 +1518,16 @@ ts_plan_expand_hypertable_chunks(Hypertable *ht, PlannerInfo *root, RelOptInfo * Assert(chunk->table_id == root->simple_rte_array[child_rtindex]->relid); ts_get_private_reloptinfo(child_rel)->cached_chunk_struct = chunk; } + + /* + * This calculation is done by make_one_rel, but it happens before our + * hypertable expansion, so we have to account for newly expanded chunks + * separately. + */ + if (!IS_DUMMY_REL(child_rel)) + { + root->total_table_pages += child_rel->pages; + } } }