mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Fix possible crash in partition-wise join.
The previous code assumed that we'd always succeed in creating child-joins for a joinrel for which partition-wise join was considered, but that's not guaranteed, at least in the case where dummy rels are involved. Ashutosh Bapat, with some wordsmithing by me. Discussion: http://postgr.es/m/CAFjFpRf8=uyMYYfeTBjWDMs1tR5t--FgOe2vKZPULxxdYQ4RNw@mail.gmail.com
This commit is contained in:
parent
1eb5d43bee
commit
f069c91a57
@ -3425,20 +3425,8 @@ generate_partition_wise_join_paths(PlannerInfo *root, RelOptInfo *rel)
|
|||||||
if (!IS_JOIN_REL(rel))
|
if (!IS_JOIN_REL(rel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/* We've nothing to do if the relation is not partitioned. */
|
||||||
* If we've already proven this join is empty, we needn't consider any
|
if (!IS_PARTITIONED_REL(rel))
|
||||||
* more paths for it.
|
|
||||||
*/
|
|
||||||
if (IS_DUMMY_REL(rel))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We've nothing to do if the relation is not partitioned. An outer join
|
|
||||||
* relation which had an empty inner relation in every pair will have the
|
|
||||||
* rest of the partitioning properties set except the child-join
|
|
||||||
* RelOptInfos. See try_partition_wise_join() for more details.
|
|
||||||
*/
|
|
||||||
if (rel->nparts <= 0 || rel->part_rels == NULL)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Guard against stack overflow due to overly deep partition hierarchy. */
|
/* Guard against stack overflow due to overly deep partition hierarchy. */
|
||||||
@ -3452,6 +3440,8 @@ generate_partition_wise_join_paths(PlannerInfo *root, RelOptInfo *rel)
|
|||||||
{
|
{
|
||||||
RelOptInfo *child_rel = part_rels[cnt_parts];
|
RelOptInfo *child_rel = part_rels[cnt_parts];
|
||||||
|
|
||||||
|
Assert(child_rel != NULL);
|
||||||
|
|
||||||
/* Add partition-wise join paths for partitioned child-joins. */
|
/* Add partition-wise join paths for partitioned child-joins. */
|
||||||
generate_partition_wise_join_paths(root, child_rel);
|
generate_partition_wise_join_paths(root, child_rel);
|
||||||
|
|
||||||
|
@ -1318,17 +1318,6 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
|||||||
if (!IS_PARTITIONED_REL(joinrel))
|
if (!IS_PARTITIONED_REL(joinrel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
* set_rel_pathlist() may not create paths in children of an empty
|
|
||||||
* partitioned table and so we can not add paths to child-joins. So, deem
|
|
||||||
* such a join as unpartitioned. When a partitioned relation is deemed
|
|
||||||
* empty because all its children are empty, dummy path will be set in
|
|
||||||
* each of the children. In such a case we could still consider the join
|
|
||||||
* as partitioned, but it might not help much.
|
|
||||||
*/
|
|
||||||
if (IS_DUMMY_REL(rel1) || IS_DUMMY_REL(rel2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since this join relation is partitioned, all the base relations
|
* Since this join relation is partitioned, all the base relations
|
||||||
* participating in this join must be partitioned and so are all the
|
* participating in this join must be partitioned and so are all the
|
||||||
@ -1360,11 +1349,6 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
|||||||
|
|
||||||
nparts = joinrel->nparts;
|
nparts = joinrel->nparts;
|
||||||
|
|
||||||
/* Allocate space to hold child-joins RelOptInfos, if not already done. */
|
|
||||||
if (!joinrel->part_rels)
|
|
||||||
joinrel->part_rels =
|
|
||||||
(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create child-join relations for this partitioned join, if those don't
|
* Create child-join relations for this partitioned join, if those don't
|
||||||
* exist. Add paths to child-joins for a pair of child relations
|
* exist. Add paths to child-joins for a pair of child relations
|
||||||
|
@ -1662,11 +1662,14 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
|||||||
*/
|
*/
|
||||||
joinrel->part_scheme = part_scheme;
|
joinrel->part_scheme = part_scheme;
|
||||||
joinrel->boundinfo = outer_rel->boundinfo;
|
joinrel->boundinfo = outer_rel->boundinfo;
|
||||||
joinrel->nparts = outer_rel->nparts;
|
|
||||||
partnatts = joinrel->part_scheme->partnatts;
|
partnatts = joinrel->part_scheme->partnatts;
|
||||||
joinrel->partexprs = (List **) palloc0(sizeof(List *) * partnatts);
|
joinrel->partexprs = (List **) palloc0(sizeof(List *) * partnatts);
|
||||||
joinrel->nullable_partexprs =
|
joinrel->nullable_partexprs =
|
||||||
(List **) palloc0(sizeof(List *) * partnatts);
|
(List **) palloc0(sizeof(List *) * partnatts);
|
||||||
|
joinrel->nparts = outer_rel->nparts;
|
||||||
|
joinrel->part_rels =
|
||||||
|
(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * joinrel->nparts);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct partition keys for the join.
|
* Construct partition keys for the join.
|
||||||
|
@ -666,13 +666,17 @@ typedef struct RelOptInfo
|
|||||||
/*
|
/*
|
||||||
* Is given relation partitioned?
|
* Is given relation partitioned?
|
||||||
*
|
*
|
||||||
* A join between two partitioned relations with same partitioning scheme
|
* It's not enough to test whether rel->part_scheme is set, because it might
|
||||||
* without any matching partitions will not have any partition in it but will
|
* be that the basic partitioning properties of the input relations matched
|
||||||
* have partition scheme set. So a relation is deemed to be partitioned if it
|
* but the partition bounds did not.
|
||||||
* has a partitioning scheme, bounds and positive number of partitions.
|
*
|
||||||
|
* We treat dummy relations as unpartitioned. We could alternatively
|
||||||
|
* treat them as partitioned, but it's not clear whether that's a useful thing
|
||||||
|
* to do.
|
||||||
*/
|
*/
|
||||||
#define IS_PARTITIONED_REL(rel) \
|
#define IS_PARTITIONED_REL(rel) \
|
||||||
((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0)
|
((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0 && \
|
||||||
|
(rel)->part_rels && !(IS_DUMMY_REL(rel)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convenience macro to make sure that a partitioned relation has all the
|
* Convenience macro to make sure that a partitioned relation has all the
|
||||||
|
@ -1217,24 +1217,31 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
|
|||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b, prt1 t3 WHERE t2.b = t3.a;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------
|
--------------------------------------------------
|
||||||
Sort
|
Hash Left Join
|
||||||
Sort Key: a, t2.b
|
Hash Cond: (t2.b = a)
|
||||||
-> Hash Left Join
|
-> Append
|
||||||
Hash Cond: (t2.b = a)
|
-> Hash Join
|
||||||
-> Append
|
Hash Cond: (t3.a = t2.b)
|
||||||
-> Seq Scan on prt2_p1 t2
|
-> Seq Scan on prt1_p1 t3
|
||||||
Filter: (a = 0)
|
-> Hash
|
||||||
-> Seq Scan on prt2_p2 t2_1
|
-> Seq Scan on prt2_p1 t2
|
||||||
Filter: (a = 0)
|
-> Hash Join
|
||||||
-> Seq Scan on prt2_p3 t2_2
|
Hash Cond: (t3_1.a = t2_1.b)
|
||||||
Filter: (a = 0)
|
-> Seq Scan on prt1_p2 t3_1
|
||||||
-> Hash
|
-> Hash
|
||||||
-> Result
|
-> Seq Scan on prt2_p2 t2_1
|
||||||
One-Time Filter: false
|
-> Hash Join
|
||||||
(14 rows)
|
Hash Cond: (t3_2.a = t2_2.b)
|
||||||
|
-> Seq Scan on prt1_p3 t3_2
|
||||||
|
-> Hash
|
||||||
|
-> Seq Scan on prt2_p3 t2_2
|
||||||
|
-> Hash
|
||||||
|
-> Result
|
||||||
|
One-Time Filter: false
|
||||||
|
(21 rows)
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
||||||
|
@ -224,7 +224,7 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
|
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b, prt1 t3 WHERE t2.b = t3.a;
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user