Skip to content

Commit

Permalink
Fix join propagation for nested joins
Browse files Browse the repository at this point in the history
Join propagation would propage join quals for nested joins even
when it was not save to do so leading to wrong query results.
  • Loading branch information
svenklemm committed Feb 4, 2021
1 parent 954c775 commit 71ffd50
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/plan_expand_hypertable.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ typedef struct CollectQualCtx
List *join_conditions;
List *propagate_conditions;
List *all_quals;
int join_level;
} CollectQualCtx;

static void propagate_join_quals(PlannerInfo *root, RelOptInfo *rel, CollectQualCtx *ctx);
Expand Down Expand Up @@ -838,13 +839,18 @@ collect_quals_walker(Node *node, CollectQualCtx *ctx)
{
FromExpr *f = castNode(FromExpr, node);
f->quals = process_quals(f->quals, ctx, false);
collect_join_quals(f->quals, ctx, false);
/* if this is a nested join we don't propagate join quals */
collect_join_quals(f->quals, ctx, ctx->join_level >= 1);
}
else if (IsA(node, JoinExpr))
{
JoinExpr *j = castNode(JoinExpr, node);
j->quals = process_quals(j->quals, ctx, IS_OUTER_JOIN(j->jointype));
collect_join_quals(j->quals, ctx, IS_OUTER_JOIN(j->jointype));
ctx->join_level++;
bool result = expression_tree_walker(node, collect_quals_walker, ctx);
ctx->join_level--;
return result;
}

/* skip processing if we found a chunks_in call for current relation */
Expand Down Expand Up @@ -1172,6 +1178,7 @@ ts_plan_expand_hypertable_chunks(Hypertable *ht, PlannerInfo *root, RelOptInfo *
.all_quals = NIL,
.join_conditions = NIL,
.propagate_conditions = NIL,
.join_level = 0,
};
Index first_chunk_index = 0;
#if PG12_GE
Expand All @@ -1195,6 +1202,8 @@ ts_plan_expand_hypertable_chunks(Hypertable *ht, PlannerInfo *root, RelOptInfo *

/* Walk the tree and find restrictions or chunk exclusion functions */
collect_quals_walker((Node *) root->parse->jointree, &ctx);
/* check join_level bookkeeping is balanced */
Assert(ctx.join_level == 0);

#if PG12_GE
rel->baserestrictinfo = remove_exclusion_fns(rel->baserestrictinfo);
Expand Down
69 changes: 69 additions & 0 deletions test/expected/plan_expand_hypertable-11.out
Original file line number Diff line number Diff line change
Expand Up @@ -2475,6 +2475,75 @@ SELECT time FROM regular_timestamptz UNION ALL SELECT time FROM metrics_timestam
Tue Feb 01 00:00:00 2000 PST
(128 rows)

-- test nested join qual propagation
:PREFIX
SELECT * FROM (
SELECT m1.time FROM metrics_timestamptz m1 FULL OUTER JOIN metrics_timestamptz_2 m2 ON true WHERE m2.time = m1.time AND m1.device_id = m2.device_id AND m2.time < '2000-01-10' AND m1.device_id = 1
) o1 FULL OUTER JOIN (
SELECT m1.time FROM metrics_timestamptz m1 FULL OUTER JOIN metrics_timestamptz_2 m2 ON true WHERE m2.time = m1.time AND m1.device_id = m2.device_id AND m2.time > '2000-01-20' AND m1.device_id = 2
) o2 ON o1.time = o2.time ORDER BY 1,2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: m1."time", m1_5."time"
-> Merge Full Join
Merge Cond: (m1_5."time" = m1."time")
-> Nested Loop
-> Merge Append
Sort Key: m2_3."time"
-> Index Scan Backward using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk m2_3
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk m2_4
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Index Scan Backward using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk m2_5
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Append
-> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk m1_5
Index Cond: ("time" = m2_3."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk m1_6
Index Cond: ("time" = m2_3."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk m1_7
Index Cond: ("time" = m2_3."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk m1_8
Index Cond: ("time" = m2_3."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk m1_9
Index Cond: ("time" = m2_3."time")
Filter: (device_id = 2)
-> Materialize
-> Nested Loop
-> Custom Scan (ChunkAppend) on metrics_timestamptz_2 m2
Order: m2."time"
-> Index Scan Backward using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk m2_1
Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 1)
-> Index Scan Backward using _hyper_7_166_chunk_metrics_timestamptz_2_time_idx on _hyper_7_166_chunk m2_2
Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 1)
-> Append
-> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk m1
Index Cond: ("time" = m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk m1_1
Index Cond: ("time" = m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk m1_2
Index Cond: ("time" = m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk m1_3
Index Cond: ("time" = m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk m1_4
Index Cond: ("time" = m2."time")
Filter: (device_id = 1)
(58 rows)

\ir include/plan_expand_hypertable_chunks_in_query.sql
-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
Expand Down
69 changes: 69 additions & 0 deletions test/expected/plan_expand_hypertable-12.out
Original file line number Diff line number Diff line change
Expand Up @@ -2425,6 +2425,75 @@ SELECT time FROM regular_timestamptz UNION ALL SELECT time FROM metrics_timestam
Tue Feb 01 00:00:00 2000 PST
(128 rows)

-- test nested join qual propagation
:PREFIX
SELECT * FROM (
SELECT o1_m1.time FROM metrics_timestamptz o1_m1 FULL OUTER JOIN metrics_timestamptz_2 o1_m2 ON true WHERE o1_m2.time = o1_m1.time AND o1_m1.device_id = o1_m2.device_id AND o1_m2.time < '2000-01-10' AND o1_m1.device_id = 1
) o1 FULL OUTER JOIN (
SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timestamptz_2 o2_m2 ON true WHERE o2_m2.time = o2_m1.time AND o2_m1.device_id = o2_m2.device_id AND o2_m2.time > '2000-01-20' AND o2_m1.device_id = 2
) o2 ON o1.time = o2.time ORDER BY 1,2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: o1_m1."time", o2_m1."time"
-> Merge Full Join
Merge Cond: (o2_m1."time" = o1_m1."time")
-> Nested Loop
-> Merge Append
Sort Key: o2_m2."time"
-> Index Scan Backward using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> 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)
Filter: (device_id = 2)
-> Index Scan Backward using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_2
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Append
-> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o2_m1
Index Cond: ("time" = o2_m2."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o2_m1_1
Index Cond: ("time" = o2_m2."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o2_m1_2
Index Cond: ("time" = o2_m2."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o2_m1_3
Index Cond: ("time" = o2_m2."time")
Filter: (device_id = 2)
-> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o2_m1_4
Index Cond: ("time" = o2_m2."time")
Filter: (device_id = 2)
-> Materialize
-> Nested Loop
-> Custom Scan (ChunkAppend) on metrics_timestamptz_2 o1_m2
Order: o1_m2."time"
-> Index Scan Backward using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1
Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)
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)
Filter: (device_id = 1)
-> Append
-> Index Scan using _hyper_6_160_chunk_metrics_timestamptz_time_idx on _hyper_6_160_chunk o1_m1
Index Cond: ("time" = o1_m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_161_chunk_metrics_timestamptz_time_idx on _hyper_6_161_chunk o1_m1_1
Index Cond: ("time" = o1_m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_162_chunk_metrics_timestamptz_time_idx on _hyper_6_162_chunk o1_m1_2
Index Cond: ("time" = o1_m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_163_chunk_metrics_timestamptz_time_idx on _hyper_6_163_chunk o1_m1_3
Index Cond: ("time" = o1_m2."time")
Filter: (device_id = 1)
-> Index Scan using _hyper_6_164_chunk_metrics_timestamptz_time_idx on _hyper_6_164_chunk o1_m1_4
Index Cond: ("time" = o1_m2."time")
Filter: (device_id = 1)
(58 rows)

\ir include/plan_expand_hypertable_chunks_in_query.sql
-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
Expand Down
69 changes: 69 additions & 0 deletions test/expected/plan_expand_hypertable-13.out
Original file line number Diff line number Diff line change
Expand Up @@ -2425,6 +2425,75 @@ SELECT time FROM regular_timestamptz UNION ALL SELECT time FROM metrics_timestam
Tue Feb 01 00:00:00 2000 PST
(128 rows)

-- test nested join qual propagation
:PREFIX
SELECT * FROM (
SELECT o1_m1.time FROM metrics_timestamptz o1_m1 FULL OUTER JOIN metrics_timestamptz_2 o1_m2 ON true WHERE o1_m2.time = o1_m1.time AND o1_m1.device_id = o1_m2.device_id AND o1_m2.time < '2000-01-10' AND o1_m1.device_id = 1
) o1 FULL OUTER JOIN (
SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timestamptz_2 o2_m2 ON true WHERE o2_m2.time = o2_m1.time AND o2_m1.device_id = o2_m2.device_id AND o2_m2.time > '2000-01-20' AND o2_m1.device_id = 2
) o2 ON o1.time = o2.time ORDER BY 1,2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: o1_m1_1."time", o2_m1_1."time"
-> Merge Full Join
Merge Cond: (o2_m1_1."time" = o1_m1_1."time")
-> Nested Loop
-> Merge Append
Sort Key: o2_m2_1."time"
-> Index Scan Backward using _hyper_7_170_chunk_metrics_timestamptz_2_time_idx on _hyper_7_170_chunk o2_m2_1
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Index Scan Backward using _hyper_7_168_chunk_metrics_timestamptz_2_time_idx on _hyper_7_168_chunk o2_m2_2
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
Filter: (device_id = 2)
-> Index Scan Backward using _hyper_7_169_chunk_metrics_timestamptz_2_time_idx on _hyper_7_169_chunk o2_m2_3
Index Cond: ("time" > 'Thu Jan 20 00:00:00 2000 PST'::timestamp with time zone)
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_1."time")
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_1."time")
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_1."time")
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_1."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_1."time")
Filter: (device_id = 2)
-> Materialize
-> Nested Loop
-> Custom Scan (ChunkAppend) on metrics_timestamptz_2 o1_m2
Order: o1_m2."time"
-> Index Scan Backward using _hyper_7_165_chunk_metrics_timestamptz_2_time_idx on _hyper_7_165_chunk o1_m2_1
Index Cond: ("time" < 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)
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)
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")
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")
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")
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")
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")
Filter: (device_id = 1)
(58 rows)

\ir include/plan_expand_hypertable_chunks_in_query.sql
-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
Expand Down
15 changes: 15 additions & 0 deletions test/sql/include/plan_expand_hypertable_query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,18 @@ SELECT time FROM regular_timestamptz UNION SELECT time FROM metrics_timestamptz

-- test UNION ALL between regular table and hypertable
SELECT time FROM regular_timestamptz UNION ALL SELECT time FROM metrics_timestamptz ORDER BY 1;

-- test nested join qual propagation
:PREFIX
SELECT * FROM (
SELECT o1_m1.time FROM metrics_timestamptz o1_m1 FULL OUTER JOIN metrics_timestamptz_2 o1_m2 ON true WHERE o1_m2.time = o1_m1.time AND o1_m1.device_id = o1_m2.device_id AND o1_m2.time < '2000-01-10' AND o1_m1.device_id = 1
) o1 FULL OUTER JOIN (
SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timestamptz_2 o2_m2 ON true WHERE o2_m2.time = o2_m1.time AND o2_m1.device_id = o2_m2.device_id AND o2_m2.time > '2000-01-20' AND o2_m1.device_id = 2
) o2 ON o1.time = o2.time ORDER BY 1,2;

:PREFIX
SELECT * FROM (
SELECT o1_m1.time FROM metrics_timestamptz o1_m1 INNER JOIN metrics_timestamptz_2 o1_m2 ON o1_m2.time = o1_m1.time AND o1_m1.device_id = o1_m2.device_id WHERE o1_m2.time < '2000-01-10' AND o1_m1.device_id = 1
) o1 FULL OUTER JOIN (
SELECT o2_m1.time FROM metrics_timestamptz o2_m1 FULL OUTER JOIN metrics_timestamptz_2 o2_m2 ON true WHERE o2_m2.time = o2_m1.time AND o2_m1.device_id = o2_m2.device_id AND o2_m2.time > '2000-01-20' AND o2_m1.device_id = 2
) o2 ON o1.time = o2.time ORDER BY 1,2;

0 comments on commit 71ffd50

Please sign in to comment.