Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: add foreign key check/cascade information in explain result #39167

Merged
merged 28 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
756ffcb
*: support explain with foreign key check/cascade
crazycs520 Nov 15, 2022
1a5a715
refine code
crazycs520 Nov 15, 2022
cee28ef
tiny fix
crazycs520 Nov 15, 2022
4c31703
add normalize plan with foreign key test
crazycs520 Nov 15, 2022
0a15163
tiny refine
crazycs520 Nov 15, 2022
a187b9e
fix ci
crazycs520 Nov 15, 2022
0776206
Merge branch 'master' into fk-explain
crazycs520 Nov 15, 2022
a64d6b5
fix ci
crazycs520 Nov 15, 2022
15bbefd
Merge branch 'fk-explain' of https://github.com/crazycs520/tidb into …
crazycs520 Nov 15, 2022
76f02b2
move test to address comment
crazycs520 Nov 17, 2022
06f6151
address comment
crazycs520 Nov 18, 2022
41cc9e7
address comment
crazycs520 Nov 18, 2022
6d68562
Merge branch 'master' of https://github.com/pingcap/tidb into fk-explain
crazycs520 Nov 18, 2022
8d453e9
make ci stable
crazycs520 Nov 18, 2022
e5f9543
Merge branch 'master' of https://github.com/pingcap/tidb into fk-explain
crazycs520 Nov 18, 2022
4c0bf4c
remove bin
crazycs520 Nov 18, 2022
bea9776
fix bug
crazycs520 Nov 18, 2022
0ba0320
add more test
crazycs520 Nov 18, 2022
c45b10c
add more test
crazycs520 Nov 18, 2022
ffbee96
Merge branch 'master' into fk-explain
crazycs520 Nov 18, 2022
ffea8f1
Merge branch 'master' into fk-explain
hawkingrei Nov 18, 2022
e541b93
Merge branch 'master' into fk-explain
crazycs520 Nov 21, 2022
267411b
Merge branch 'master' into fk-explain
crazycs520 Nov 21, 2022
7f816d4
Merge branch 'master' into fk-explain
crazycs520 Nov 22, 2022
0798894
refine test
crazycs520 Nov 22, 2022
2bfdef2
Merge branch 'master' into fk-explain
crazycs520 Nov 22, 2022
c4d4ed8
Merge branch 'master' into fk-explain
crazycs520 Nov 22, 2022
e909926
Merge branch 'master' into fk-explain
ti-chi-bot Nov 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions cmd/explaintest/r/explain_foreign_key.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
set @@global.tidb_enable_foreign_key=1;
crazycs520 marked this conversation as resolved.
Show resolved Hide resolved
set @@foreign_key_checks=1;
use test;
drop table if exists t1,t2;
create table t1 (id int key);
create table t2 (id int key, foreign key fk(id) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);
create table t3 (id int, unique index idx(id));
create table t4 (id int, index idx_id(id),foreign key fk(id) references t3(id));
create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));
create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE CASCADE, foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);
explain format = 'brief' insert into t2 values (1);
id estRows task access object operator info
Insert N/A root N/A
└─Foreign_Key_Check 0.00 root table:t1 foreign_key:fk, check_exist
explain format = 'brief' update t2 set id=id+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─Point_Get 1.00 root table:t2 handle:1, lock
└─Foreign_Key_Check 0.00 root table:t1 foreign_key:fk, check_exist
explain format = 'brief' delete from t1 where id > 1;
id estRows task access object operator info
Delete N/A root N/A
├─SelectLock 3333.33 root for update 0
│ └─TableReader 3333.33 root data:TableRangeScan
│ └─TableRangeScan 3333.33 cop[tikv] table:t1 range:(1,+inf], keep order:false, stats:pseudo
└─Foreign_Key_Cascade 0.00 root table:t2 foreign_key:fk, on_delete:CASCADE
explain format = 'brief' update t1 set id=id+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─Point_Get 1.00 root table:t1 handle:1, lock
└─Foreign_Key_Cascade 0.00 root table:t2 foreign_key:fk, on_update:CASCADE
explain format = 'brief' insert into t1 values (1);
id estRows task access object operator info
Insert N/A root N/A
explain format = 'brief' insert into t1 values (1) on duplicate key update id = 100;
id estRows task access object operator info
Insert N/A root N/A
└─Foreign_Key_Cascade 0.00 root table:t2 foreign_key:fk, on_update:CASCADE
explain format = 'brief' insert into t4 values (1);
id estRows task access object operator info
Insert N/A root N/A
└─Foreign_Key_Check 0.00 root table:t3, index:idx foreign_key:fk, check_exist
explain format = 'brief' update t4 set id=id+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─SelectLock 10.00 root for update 0
│ └─IndexReader 10.00 root index:IndexRangeScan
│ └─IndexRangeScan 10.00 cop[tikv] table:t4, index:idx_id(id) range:[1,1], keep order:false, stats:pseudo
└─Foreign_Key_Check 0.00 root table:t3, index:idx foreign_key:fk, check_exist
explain format = 'brief' delete from t3 where id > 1;
id estRows task access object operator info
Delete N/A root N/A
├─SelectLock 3333.33 root for update 0
│ └─IndexReader 3333.33 root index:IndexRangeScan
│ └─IndexRangeScan 3333.33 cop[tikv] table:t3, index:idx(id) range:(1,+inf], keep order:false, stats:pseudo
└─Foreign_Key_Check 0.00 root table:t4, index:idx_id foreign_key:fk, check_not_exist
explain format = 'brief' update t3 set id=id+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─Point_Get 1.00 root table:t3, index:idx(id) lock
└─Foreign_Key_Check 0.00 root table:t4, index:idx_id foreign_key:fk, check_not_exist
explain format = 'brief' insert into t3 values (1);
id estRows task access object operator info
Insert N/A root N/A
explain format = 'brief' insert into t3 values (1) on duplicate key update id = 100;
id estRows task access object operator info
Insert N/A root N/A
└─Foreign_Key_Check 0.00 root table:t4, index:idx_id foreign_key:fk, check_not_exist
explain format = 'brief' insert into t6 values (1,1,1);
id estRows task access object operator info
Insert N/A root N/A
├─Foreign_Key_Check 0.00 root table:t5 foreign_key:fk_1, check_exist
├─Foreign_Key_Check 0.00 root table:t5, index:idx2 foreign_key:fk_2, check_exist
└─Foreign_Key_Check 0.00 root table:t5, index:idx3 foreign_key:fk_3, check_exist
explain format = 'brief' update t6 set id=id+1, id3=id2+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─SelectLock 10.00 root for update 0
│ └─IndexLookUp 10.00 root
│ ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t6, index:idx_id(id) range:[1,1], keep order:false, stats:pseudo
│ └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t6 keep order:false, stats:pseudo
├─Foreign_Key_Check 0.00 root table:t5 foreign_key:fk_1, check_exist
└─Foreign_Key_Check 0.00 root table:t5, index:idx3 foreign_key:fk_3, check_exist
explain format = 'brief' delete from t5 where id > 1;
id estRows task access object operator info
Delete N/A root N/A
├─SelectLock 3333.33 root for update 0
│ └─TableReader 3333.33 root data:TableRangeScan
│ └─TableRangeScan 3333.33 cop[tikv] table:t5 range:(1,+inf], keep order:false, stats:pseudo
├─Foreign_Key_Check 0.00 root table:t6, index:idx_id2 foreign_key:fk_2, check_not_exist
├─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id foreign_key:fk_1, on_delete:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t6, index:fk_3 foreign_key:fk_3, on_delete:CASCADE
explain format = 'brief' update t5 set id=id+1, id2=id2+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─Point_Get 1.00 root table:t5 handle:1, lock
├─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id foreign_key:fk_1, on_update:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id2 foreign_key:fk_2, on_update:CASCADE
explain format = 'brief' update t5 set id=id+1, id2=id2+1, id3=id3+1 where id = 1;
id estRows task access object operator info
Update N/A root N/A
├─Point_Get 1.00 root table:t5 handle:1, lock
├─Foreign_Key_Check 0.00 root table:t6, index:fk_3 foreign_key:fk_3, check_not_exist
├─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id foreign_key:fk_1, on_update:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id2 foreign_key:fk_2, on_update:CASCADE
explain format = 'brief' insert into t5 values (1,1,1);
id estRows task access object operator info
Insert N/A root N/A
explain format = 'brief' insert into t5 values (1,1,1) on duplicate key update id = 100, id3=100;
id estRows task access object operator info
Insert N/A root N/A
├─Foreign_Key_Check 0.00 root table:t6, index:fk_3 foreign_key:fk_3, check_not_exist
└─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id foreign_key:fk_1, on_update:CASCADE
explain format = 'brief' insert into t5 values (1,1,1) on duplicate key update id = 100, id2=100, id3=100;
id estRows task access object operator info
Insert N/A root N/A
├─Foreign_Key_Check 0.00 root table:t6, index:fk_3 foreign_key:fk_3, check_not_exist
├─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id foreign_key:fk_1, on_update:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t6, index:idx_id2 foreign_key:fk_2, on_update:CASCADE
drop table if exists t1,t2,t3,t4,t5,t6;
drop table if exists t_1,t_2,t_3,t_4;
create table t_1 (id int key);
create table t_2 (id int key);
create table t_3 (id int key, id2 int, foreign key fk_1(id) references t_1(id), foreign key fk_2(id2) references t_1(id), foreign key fk_3(id) references t_2(id) ON UPDATE CASCADE ON DELETE CASCADE);
create table t_4 (id int key, id2 int, foreign key fk_1(id) references t_2(id), foreign key fk_2(id2) references t_1(id), foreign key fk_3(id) references t_1(id) ON UPDATE CASCADE ON DELETE CASCADE);
explain format = 'brief' update t_1,t_2 set t_1.id=2,t_2.id=2 where t_1.id=t_2.id and t_1.id=1;
id estRows task access object operator info
Update N/A root N/A
├─HashJoin 1.00 root CARTESIAN inner join
│ ├─Point_Get(Build) 1.00 root table:t_2 handle:1
│ └─Point_Get(Probe) 1.00 root table:t_1 handle:1
├─Foreign_Key_Check 0.00 root table:t_3 foreign_key:fk_1, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_3, index:fk_2 foreign_key:fk_2, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_4, index:fk_2 foreign_key:fk_2, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_4 foreign_key:fk_1, check_not_exist
├─Foreign_Key_Cascade 0.00 root table:t_4 foreign_key:fk_3, on_update:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t_3 foreign_key:fk_3, on_update:CASCADE
explain format = 'brief' delete t_1,t_2 from t_1 join t_2 where t_1.id=t_2.id and t_1.id > 0;
id estRows task access object operator info
Delete N/A root N/A
├─MergeJoin 4166.67 root inner join, left key:test.t_1.id, right key:test.t_2.id
│ ├─TableReader(Build) 3333.33 root data:TableRangeScan
│ │ └─TableRangeScan 3333.33 cop[tikv] table:t_2 range:(0,+inf], keep order:true, stats:pseudo
│ └─TableReader(Probe) 3333.33 root data:TableRangeScan
│ └─TableRangeScan 3333.33 cop[tikv] table:t_1 range:(0,+inf], keep order:true, stats:pseudo
├─Foreign_Key_Check 0.00 root table:t_3 foreign_key:fk_1, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_3, index:fk_2 foreign_key:fk_2, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_4, index:fk_2 foreign_key:fk_2, check_not_exist
├─Foreign_Key_Check 0.00 root table:t_4 foreign_key:fk_1, check_not_exist
├─Foreign_Key_Cascade 0.00 root table:t_4 foreign_key:fk_3, on_delete:CASCADE
└─Foreign_Key_Cascade 0.00 root table:t_3 foreign_key:fk_3, on_delete:CASCADE
set @@foreign_key_checks=0;
explain format = 'brief' update t_1,t_2 set t_1.id=2,t_2.id=2 where t_1.id=t_2.id and t_1.id=1;
id estRows task access object operator info
Update N/A root N/A
└─HashJoin 1.00 root CARTESIAN inner join
├─Point_Get(Build) 1.00 root table:t_2 handle:1
└─Point_Get(Probe) 1.00 root table:t_1 handle:1
explain format = 'brief' delete t_1,t_2 from t_1 join t_2 where t_1.id=t_2.id and t_1.id > 0;
id estRows task access object operator info
Delete N/A root N/A
└─MergeJoin 4166.67 root inner join, left key:test.t_1.id, right key:test.t_2.id
├─TableReader(Build) 3333.33 root data:TableRangeScan
│ └─TableRangeScan 3333.33 cop[tikv] table:t_2 range:(0,+inf], keep order:true, stats:pseudo
└─TableReader(Probe) 3333.33 root data:TableRangeScan
└─TableRangeScan 3333.33 cop[tikv] table:t_1 range:(0,+inf], keep order:true, stats:pseudo
drop table if exists t_1,t_2,t_3,t_4;
set @@global.tidb_enable_foreign_key=0;
set @@foreign_key_checks=0;
47 changes: 47 additions & 0 deletions cmd/explaintest/t/explain_foreign_key.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
set @@global.tidb_enable_foreign_key=1;
set @@foreign_key_checks=1;
use test;
drop table if exists t1,t2;
create table t1 (id int key);
create table t2 (id int key, foreign key fk(id) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);
create table t3 (id int, unique index idx(id));
create table t4 (id int, index idx_id(id),foreign key fk(id) references t3(id));
create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));
create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE CASCADE, foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);

explain format = 'brief' insert into t2 values (1);
explain format = 'brief' update t2 set id=id+1 where id = 1;
explain format = 'brief' delete from t1 where id > 1;
explain format = 'brief' update t1 set id=id+1 where id = 1;
explain format = 'brief' insert into t1 values (1);
explain format = 'brief' insert into t1 values (1) on duplicate key update id = 100;
explain format = 'brief' insert into t4 values (1);
explain format = 'brief' update t4 set id=id+1 where id = 1;
explain format = 'brief' delete from t3 where id > 1;
explain format = 'brief' update t3 set id=id+1 where id = 1;
explain format = 'brief' insert into t3 values (1);
explain format = 'brief' insert into t3 values (1) on duplicate key update id = 100;
explain format = 'brief' insert into t6 values (1,1,1);
explain format = 'brief' update t6 set id=id+1, id3=id2+1 where id = 1;
explain format = 'brief' delete from t5 where id > 1;
explain format = 'brief' update t5 set id=id+1, id2=id2+1 where id = 1;
explain format = 'brief' update t5 set id=id+1, id2=id2+1, id3=id3+1 where id = 1;
explain format = 'brief' insert into t5 values (1,1,1);
explain format = 'brief' insert into t5 values (1,1,1) on duplicate key update id = 100, id3=100;
explain format = 'brief' insert into t5 values (1,1,1) on duplicate key update id = 100, id2=100, id3=100;
drop table if exists t1,t2,t3,t4,t5,t6;

drop table if exists t_1,t_2,t_3,t_4;
create table t_1 (id int key);
create table t_2 (id int key);
create table t_3 (id int key, id2 int, foreign key fk_1(id) references t_1(id), foreign key fk_2(id2) references t_1(id), foreign key fk_3(id) references t_2(id) ON UPDATE CASCADE ON DELETE CASCADE);
create table t_4 (id int key, id2 int, foreign key fk_1(id) references t_2(id), foreign key fk_2(id2) references t_1(id), foreign key fk_3(id) references t_1(id) ON UPDATE CASCADE ON DELETE CASCADE);

explain format = 'brief' update t_1,t_2 set t_1.id=2,t_2.id=2 where t_1.id=t_2.id and t_1.id=1;
explain format = 'brief' delete t_1,t_2 from t_1 join t_2 where t_1.id=t_2.id and t_1.id > 0;
set @@foreign_key_checks=0;
explain format = 'brief' update t_1,t_2 set t_1.id=2,t_2.id=2 where t_1.id=t_2.id and t_1.id=1;
explain format = 'brief' delete t_1,t_2 from t_1 join t_2 where t_1.id=t_2.id and t_1.id > 0;
drop table if exists t_1,t_2,t_3,t_4;
set @@global.tidb_enable_foreign_key=0;
set @@foreign_key_checks=0;
8 changes: 8 additions & 0 deletions planner/core/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ func NormalizeFlatPlan(flat *FlatPhysicalPlan) (normalized string, digest *parse
d.buf.Grow(30 * len(selectPlan))
depthOffset := len(flat.Main) - len(selectPlan)
for _, op := range selectPlan {
switch op.Origin.(type) {
case *FKCheck, *FKCascade:
// Generate plan digest doesn't need to contain the foreign key check/cascade plan, so just break the loop.
continue
}
taskTypeInfo := plancodec.EncodeTaskTypeForNormalize(op.IsRoot, op.StoreType)
p := op.Origin.(PhysicalPlan)
plancodec.NormalizePlanNode(
Expand All @@ -287,6 +292,9 @@ func NormalizeFlatPlan(flat *FlatPhysicalPlan) (normalized string, digest *parse
)
}
normalized = d.buf.String()
if len(normalized) == 0 {
return "", parser.NewDigest(nil)
}
_, err := d.hasher.Write(d.buf.Bytes())
if err != nil {
panic(err)
Expand Down
55 changes: 52 additions & 3 deletions planner/core/flat_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package core

import (
"sort"

"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/util/texttree"
)
Expand Down Expand Up @@ -348,26 +350,29 @@ func (f *FlatPhysicalPlan) flattenRecursively(p Plan, info *operatorCtx, target
if plan.SelectPlan != nil {
childCtx.isRoot = true
childCtx.label = Empty
childCtx.isLastChild = true
childCtx.isLastChild = len(plan.FKChecks) == 0 && len(plan.FKCascades) == 0
target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
childIdxs = append(childIdxs, childIdx)
}
target, childIdxs = f.flattenForeignKeyChecksAndCascades(childCtx, target, childIdxs, plan.FKChecks, plan.FKCascades, true)
case *Update:
if plan.SelectPlan != nil {
childCtx.isRoot = true
childCtx.label = Empty
childCtx.isLastChild = true
childCtx.isLastChild = len(plan.FKChecks) == 0 && len(plan.FKCascades) == 0
target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
childIdxs = append(childIdxs, childIdx)
}
target, childIdxs = f.flattenForeignKeyChecksAndCascadesMap(childCtx, target, childIdxs, plan.FKChecks, plan.FKCascades)
case *Delete:
if plan.SelectPlan != nil {
childCtx.isRoot = true
childCtx.label = Empty
childCtx.isLastChild = true
childCtx.isLastChild = len(plan.FKChecks) == 0 && len(plan.FKCascades) == 0
target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
childIdxs = append(childIdxs, childIdx)
}
target, childIdxs = f.flattenForeignKeyChecksAndCascadesMap(childCtx, target, childIdxs, plan.FKChecks, plan.FKCascades)
case *Execute:
f.InExecute = true
if plan.Plan != nil {
Expand Down Expand Up @@ -401,6 +406,50 @@ func (f *FlatPhysicalPlan) flattenRecursively(p Plan, info *operatorCtx, target
return target, idx
}

func (f *FlatPhysicalPlan) flattenForeignKeyChecksAndCascadesMap(childCtx *operatorCtx, target FlatPlanTree, childIdxs []int, fkChecksMap map[int64][]*FKCheck, fkCascadesMap map[int64][]*FKCascade) (FlatPlanTree, []int) {
tids := make([]int64, 0, len(fkChecksMap))
for tid := range fkChecksMap {
tids = append(tids, tid)
}
// Sort by table id for explain result stable.
sort.Slice(tids, func(i, j int) bool {
return tids[i] < tids[j]
})
for i, tid := range tids {
target, childIdxs = f.flattenForeignKeyChecksAndCascades(childCtx, target, childIdxs, fkChecksMap[tid], nil, len(fkCascadesMap) == 0 && i == len(tids)-1)
}
tids = tids[:0]
for tid := range fkCascadesMap {
tids = append(tids, tid)
}
sort.Slice(tids, func(i, j int) bool {
return tids[i] < tids[j]
})
for i, tid := range tids {
target, childIdxs = f.flattenForeignKeyChecksAndCascades(childCtx, target, childIdxs, nil, fkCascadesMap[tid], i == len(tids)-1)
}
return target, childIdxs
}

func (f *FlatPhysicalPlan) flattenForeignKeyChecksAndCascades(childCtx *operatorCtx, target FlatPlanTree, childIdxs []int, fkChecks []*FKCheck, fkCascades []*FKCascade, isLast bool) (FlatPlanTree, []int) {
var childIdx int
for i, fkCheck := range fkChecks {
childCtx.isRoot = true
childCtx.label = Empty
childCtx.isLastChild = isLast && len(fkCascades) == 0 && i == len(fkChecks)-1
target, childIdx = f.flattenRecursively(fkCheck, childCtx, target)
childIdxs = append(childIdxs, childIdx)
}
for i, fkCascade := range fkCascades {
childCtx.isRoot = true
childCtx.label = Empty
childCtx.isLastChild = isLast && i == len(fkCascades)-1
target, childIdx = f.flattenRecursively(fkCascade, childCtx, target)
childIdxs = append(childIdxs, childIdx)
}
return target, childIdxs
}

func (f *FlatPhysicalPlan) flattenCTERecursively(cteDef *CTEDefinition, info *operatorCtx, target FlatPlanTree) FlatPlanTree {
flat := f.flattenSingle(cteDef, info)
if flat != nil {
Expand Down
Loading