mirror of https://github.com/citusdata/citus.git
Columnar: add method ReparameterizeCustomPathByChild. (#5275)
When performing a partition-wise join, the planner will adjust paths parameterized by the parent rel to instead parameterize by the child rel directly. When this reparameterization happens, we also need to adjust the join quals to reference the child rather than the parent. Fixes #5257.pull/5261/head
parent
ea61efb63a
commit
d48ceee238
|
@ -104,6 +104,9 @@ static Plan * ColumnarScanPath_PlanCustomPath(PlannerInfo *root,
|
|||
List *tlist,
|
||||
List *clauses,
|
||||
List *custom_plans);
|
||||
static List * ColumnarScanPath_ReparameterizeCustomPathByChild(PlannerInfo *root,
|
||||
List *custom_private,
|
||||
RelOptInfo *child_rel);
|
||||
static Node * ColumnarScan_CreateCustomScanState(CustomScan *cscan);
|
||||
|
||||
static void ColumnarScan_BeginCustomScan(CustomScanState *node, EState *estate,
|
||||
|
@ -141,6 +144,7 @@ static int ColumnarPlannerDebugLevel = DEBUG3;
|
|||
const struct CustomPathMethods ColumnarScanPathMethods = {
|
||||
.CustomName = "ColumnarScan",
|
||||
.PlanCustomPath = ColumnarScanPath_PlanCustomPath,
|
||||
.ReparameterizeCustomPathByChild = ColumnarScanPath_ReparameterizeCustomPathByChild,
|
||||
};
|
||||
|
||||
const struct CustomScanMethods ColumnarScanScanMethods = {
|
||||
|
@ -1316,6 +1320,66 @@ ColumnarScanPath_PlanCustomPath(PlannerInfo *root,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* ReparameterizeMutator changes all varnos referencing the topmost parent of
|
||||
* child_rel to instead reference child_rel directly.
|
||||
*/
|
||||
static Node *
|
||||
ReparameterizeMutator(Node *node, RelOptInfo *child_rel)
|
||||
{
|
||||
if (node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = castNode(Var, node);
|
||||
if (bms_is_member(var->varno, child_rel->top_parent_relids))
|
||||
{
|
||||
var = copyObject(var);
|
||||
var->varno = child_rel->relid;
|
||||
}
|
||||
return (Node *) var;
|
||||
}
|
||||
|
||||
if (IsA(node, RestrictInfo))
|
||||
{
|
||||
RestrictInfo *rinfo = castNode(RestrictInfo, node);
|
||||
rinfo = copyObject(rinfo);
|
||||
rinfo->clause = (Expr *) expression_tree_mutator(
|
||||
(Node *) rinfo->clause, ReparameterizeMutator, (void *) child_rel);
|
||||
return (Node *) rinfo;
|
||||
}
|
||||
return expression_tree_mutator(node, ReparameterizeMutator,
|
||||
(void *) child_rel);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ColumnarScanPath_ReparameterizeCustomPathByChild is a method called when a
|
||||
* path is reparameterized directly to a child relation, rather than the
|
||||
* top-level parent.
|
||||
*
|
||||
* For instance, let there be a join of two partitioned columnar relations PX
|
||||
* and PY. A path for a ColumnarScan of PY3 might be parameterized by PX so
|
||||
* that the join qual "PY3.a = PX.a" (referencing the parent PX) can be pushed
|
||||
* down. But if the planner decides on a partition-wise join, then the path
|
||||
* will be reparameterized on the child table PX3 directly.
|
||||
*
|
||||
* When that happens, we need to update all Vars in the pushed-down quals to
|
||||
* reference PX3, not PX, to match the new parameterization. This method
|
||||
* notifies us that it needs to be done, and allows us to update the
|
||||
* information in custom_private.
|
||||
*/
|
||||
static List *
|
||||
ColumnarScanPath_ReparameterizeCustomPathByChild(PlannerInfo *root,
|
||||
List *custom_private,
|
||||
RelOptInfo *child_rel)
|
||||
{
|
||||
return (List *) ReparameterizeMutator((Node *) custom_private, child_rel);
|
||||
}
|
||||
|
||||
|
||||
static Node *
|
||||
ColumnarScan_CreateCustomScanState(CustomScan *cscan)
|
||||
{
|
||||
|
|
|
@ -414,3 +414,104 @@ DROP TABLE i_col CASCADE;
|
|||
NOTICE: drop cascades to 2 other objects
|
||||
DETAIL: drop cascades to table ij_col_row
|
||||
drop cascades to table ij_col_col
|
||||
--
|
||||
-- https://github.com/citusdata/citus/issues/5257
|
||||
--
|
||||
set default_table_access_method to columnar;
|
||||
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
|
||||
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
|
||||
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
|
||||
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
|
||||
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
|
||||
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
|
||||
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
|
||||
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
|
||||
SET enable_partitionwise_join to true;
|
||||
EXPLAIN (costs off, timing off, summary off)
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------
|
||||
Sort
|
||||
Sort Key: t1.a
|
||||
-> Append
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p1 t1_1
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2_1.a = t3_1.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p1 t2_1
|
||||
Filter: (t1_1.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1_1.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p1 t3_1
|
||||
Columnar Projected Columns: a, b
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p2 t1_2
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2_2.a = t3_2.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p2 t2_2
|
||||
Filter: (t1_2.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1_2.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p2 t3_2
|
||||
Columnar Projected Columns: a, b
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p3 t1_3
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2_3.a = t3_3.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p3 t2_3
|
||||
Filter: (t1_3.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1_3.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p3 t3_3
|
||||
Columnar Projected Columns: a, b
|
||||
(45 rows)
|
||||
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
a | b | c | t2a | t3a | least
|
||||
---------------------------------------------------------------------
|
||||
0 | 0 | 0000 | 0 | 0 | 0
|
||||
50 | 0 | 0050 | | |
|
||||
100 | 0 | 0100 | | |
|
||||
150 | 0 | 0150 | 150 | 0 | 150
|
||||
200 | 0 | 0200 | | |
|
||||
250 | 0 | 0250 | | |
|
||||
300 | 0 | 0300 | 300 | 0 | 300
|
||||
350 | 0 | 0350 | | |
|
||||
400 | 0 | 0400 | | |
|
||||
450 | 0 | 0450 | 450 | 0 | 450
|
||||
500 | 0 | 0500 | | |
|
||||
550 | 0 | 0550 | | |
|
||||
(12 rows)
|
||||
|
||||
set default_table_access_method to default;
|
||||
SET enable_partitionwise_join to default;
|
||||
DROP TABLE prt1;
|
||||
DROP TABLE prt2;
|
||||
|
|
|
@ -414,3 +414,104 @@ DROP TABLE i_col CASCADE;
|
|||
NOTICE: drop cascades to 2 other objects
|
||||
DETAIL: drop cascades to table ij_col_row
|
||||
drop cascades to table ij_col_col
|
||||
--
|
||||
-- https://github.com/citusdata/citus/issues/5257
|
||||
--
|
||||
set default_table_access_method to columnar;
|
||||
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
|
||||
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
|
||||
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
|
||||
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
|
||||
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
|
||||
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
|
||||
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
|
||||
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
|
||||
SET enable_partitionwise_join to true;
|
||||
EXPLAIN (costs off, timing off, summary off)
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------
|
||||
Sort
|
||||
Sort Key: t1.a
|
||||
-> Append
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p1 t1
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2.a = t3.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p1 t2
|
||||
Filter: (t1.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p1 t3
|
||||
Columnar Projected Columns: a, b
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p2 t1_1
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2_1.a = t3_1.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p2 t2_1
|
||||
Filter: (t1_1.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1_1.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p2 t3_1
|
||||
Columnar Projected Columns: a, b
|
||||
-> Nested Loop Left Join
|
||||
-> Custom Scan (ColumnarScan) on prt1_p3 t1_2
|
||||
Filter: (b = 0)
|
||||
Columnar Projected Columns: a, b, c
|
||||
Columnar Chunk Group Filters: (b = 0)
|
||||
-> Hash Join
|
||||
Hash Cond: (t2_2.a = t3_2.b)
|
||||
-> Custom Scan (ColumnarScan) on prt1_p3 t2_2
|
||||
Filter: (t1_2.a = a)
|
||||
Columnar Projected Columns: a
|
||||
Columnar Chunk Group Filters: (t1_2.a = a)
|
||||
-> Hash
|
||||
-> Custom Scan (ColumnarScan) on prt2_p3 t3_2
|
||||
Columnar Projected Columns: a, b
|
||||
(45 rows)
|
||||
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
a | b | c | t2a | t3a | least
|
||||
---------------------------------------------------------------------
|
||||
0 | 0 | 0000 | 0 | 0 | 0
|
||||
50 | 0 | 0050 | | |
|
||||
100 | 0 | 0100 | | |
|
||||
150 | 0 | 0150 | 150 | 0 | 150
|
||||
200 | 0 | 0200 | | |
|
||||
250 | 0 | 0250 | | |
|
||||
300 | 0 | 0300 | 300 | 0 | 300
|
||||
350 | 0 | 0350 | | |
|
||||
400 | 0 | 0400 | | |
|
||||
450 | 0 | 0450 | 450 | 0 | 450
|
||||
500 | 0 | 0500 | | |
|
||||
550 | 0 | 0550 | | |
|
||||
(12 rows)
|
||||
|
||||
set default_table_access_method to default;
|
||||
SET enable_partitionwise_join to default;
|
||||
DROP TABLE prt1;
|
||||
DROP TABLE prt2;
|
||||
|
|
|
@ -146,3 +146,46 @@ DROP TABLE ij_row_row;
|
|||
|
||||
DROP TABLE i_row CASCADE;
|
||||
DROP TABLE i_col CASCADE;
|
||||
|
||||
--
|
||||
-- https://github.com/citusdata/citus/issues/5257
|
||||
--
|
||||
|
||||
set default_table_access_method to columnar;
|
||||
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
|
||||
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
|
||||
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
|
||||
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
|
||||
|
||||
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
|
||||
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
|
||||
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
|
||||
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
|
||||
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
|
||||
|
||||
SET enable_partitionwise_join to true;
|
||||
|
||||
EXPLAIN (costs off, timing off, summary off)
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
|
||||
SELECT * FROM
|
||||
prt1 t1 LEFT JOIN LATERAL
|
||||
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b)
|
||||
FROM prt1 t2
|
||||
JOIN prt2 t3 ON (t2.a = t3.b)
|
||||
) ss
|
||||
ON t1.a = ss.t2a WHERE t1.b = 0
|
||||
ORDER BY t1.a;
|
||||
|
||||
set default_table_access_method to default;
|
||||
SET enable_partitionwise_join to default;
|
||||
DROP TABLE prt1;
|
||||
DROP TABLE prt2;
|
||||
|
|
Loading…
Reference in New Issue