-
Notifications
You must be signed in to change notification settings - Fork 6.2k
planner: correlate subquery rule #66206
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
Merged
Merged
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
da93b6f
planner: correlate subquery rule (wip)
terry1purcell 171333a
review comments1
terry1purcell fca2997
add testcases1
terry1purcell 1d7e38a
add testcases2
terry1purcell 0f6c877
Merge branch 'master' into correlate
terry1purcell 62df8f8
refactor for order
terry1purcell 5ba3cca
refactor for cost based evaluation
terry1purcell a0631a5
refactor for 2nd customer example
terry1purcell 2a39db6
increase test coverage
terry1purcell c1a3d75
new pantheon review comments
terry1purcell 88efc6a
update bazel
terry1purcell 0511568
hint fix
terry1purcell a807f60
pushdown hint fix
terry1purcell 7fb0ecc
Merge branch 'master' into correlate
terry1purcell 14ff48e
Merge branch 'master' into correlate
terry1purcell 155437a
refactor after alternative plan PR implemented
terry1purcell 261c6e8
build error
terry1purcell b4b1be2
review comments after refactor
terry1purcell 88bfc9b
claude review
terry1purcell 9e14702
add parallel apply
terry1purcell ac97f9e
import reorder
terry1purcell e53692a
review comments
terry1purcell 7d49f71
copilot review comments
terry1purcell 4c32338
move clones per review
terry1purcell 6121e3a
Merge branch 'pingcap:master' into correlate
terry1purcell dd0f84f
review comments2
terry1purcell 448665b
review comments3
terry1purcell 34bbc9e
Merge branch 'master' into correlate
terry1purcell be086e8
update bazel
terry1purcell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright 2025 PingCAP, Inc. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package rule | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/pingcap/tidb/pkg/testkit" | ||
| "github.com/pingcap/tidb/pkg/testkit/testdata" | ||
| ) | ||
|
|
||
| func TestCorrelate(tt *testing.T) { | ||
| testkit.RunTestUnderCascades(tt, func(t *testing.T, tk *testkit.TestKit, cascades, caller string) { | ||
| tk.MustExec("use test") | ||
| tk.MustExec("drop table if exists t1, t2, t3") | ||
| tk.MustExec("create table t1 (a int, b int, key(a))") | ||
| tk.MustExec("create table t2 (a int, b int, key(a))") | ||
| tk.MustExec("create table t3 (a int, b int, key(a))") | ||
| tk.MustExec("insert into t1 values (1,1),(2,2),(3,3)") | ||
| tk.MustExec("insert into t2 values (1,10),(2,20)") | ||
| tk.MustExec("insert into t3 values (10,1),(20,2)") | ||
|
|
||
| // Enable the correlate rule. | ||
| tk.MustExec("set tidb_opt_enable_correlate_subquery = ON") | ||
|
|
||
| var input []string | ||
| var output []struct { | ||
| SQL string | ||
| Plan []string | ||
| Result []string | ||
| } | ||
| suite := GetCorrelateSuiteData() | ||
| suite.LoadTestCases(t, &input, &output, cascades, caller) | ||
| for i, sql := range input { | ||
| testdata.OnRecord(func() { | ||
| output[i].SQL = sql | ||
| output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + sql).Rows()) | ||
| output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(sql).Rows()) | ||
| }) | ||
| tk.MustQuery("explain format = 'brief' " + sql).Check(testkit.Rows(output[i].Plan...)) | ||
| tk.MustQuery(sql).Check(testkit.Rows(output[i].Result...)) | ||
| } | ||
| }) | ||
| } | ||
17 changes: 17 additions & 0 deletions
17
pkg/planner/core/casetest/rule/testdata/correlate_suite_in.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| [ | ||
| { | ||
| "name": "TestCorrelate", | ||
| "cases": [ | ||
| "select * from t1 where exists (select 1 from t2 where t2.a = t1.a)", | ||
| "select * from t1 where not exists (select 1 from t2 where t2.a = t1.a)", | ||
| "select * from t1 where a in (select a from t2)", | ||
| "select * from t1 where exists (select 1 from t2)", | ||
| "select * from t1 where a not in (select a from t2)", | ||
| "select * from t1 where exists (select 1 from t2 where t2.a > t1.a)", | ||
| "select * from t1 where exists (select 1 from t2 where t2.a = t1.a and t2.b > t1.b)", | ||
| "select * from t1 where exists (select /*+ NO_DECORRELATE() */ 1 from t2 where t2.a = t1.a)", | ||
| "select * from t1 where a in (select t2.a from t2 inner join t3 on t3.a = t2.b where t3.b > 0)", | ||
| "select * from t1 where a in (select a from t2) order by a limit 10" | ||
| ] | ||
| } | ||
| ] |
168 changes: 168 additions & 0 deletions
168
pkg/planner/core/casetest/rule/testdata/correlate_suite_out.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| [ | ||
| { | ||
| "Name": "TestCorrelate", | ||
| "Cases": [ | ||
| { | ||
| "SQL": "select * from t1 where exists (select 1 from t2 where t2.a = t1.a)", | ||
| "Plan": [ | ||
| "Apply 10000.00 root CARTESIAN semi join, left side:TableReader", | ||
| "├─TableReader(Build) 10000.00 root data:TableFullScan", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "└─Limit(Probe) 10000.00 root offset:0, count:1", | ||
| " └─IndexReader 10000.00 root index:Limit", | ||
| " └─Limit 10000.00 cop[tikv] offset:0, count:1", | ||
| " └─IndexRangeScan 10000.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where not exists (select 1 from t2 where t2.a = t1.a)", | ||
| "Plan": [ | ||
| "Apply 10000.00 root CARTESIAN anti semi join, left side:TableReader", | ||
| "├─TableReader(Build) 10000.00 root data:TableFullScan", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "└─Limit(Probe) 10000.00 root offset:0, count:1", | ||
| " └─IndexReader 10000.00 root index:Limit", | ||
| " └─Limit 10000.00 cop[tikv] offset:0, count:1", | ||
| " └─IndexRangeScan 10000.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "3 3" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where a in (select a from t2)", | ||
| "Plan": [ | ||
| "HashJoin 7992.00 root semi join, left side:TableReader, equal:[eq(test.t1.a, test.t2.a)]", | ||
| "├─IndexReader(Build) 9990.00 root index:IndexFullScan", | ||
| "│ └─IndexFullScan 9990.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo", | ||
| "└─TableReader(Probe) 9990.00 root data:Selection", | ||
| " └─Selection 9990.00 cop[tikv] not(isnull(test.t1.a))", | ||
| " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where exists (select 1 from t2)", | ||
| "Plan": [ | ||
| "TableReader 10000.00 root data:TableFullScan", | ||
| "└─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "ScalarSubQuery N/A root Output: ScalarQueryCol#10, ScalarQueryCol#11, ScalarQueryCol#12, ScalarQueryCol#13", | ||
| "└─TableReader 10000.00 root data:TableFullScan", | ||
| " └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2", | ||
| "3 3" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where a not in (select a from t2)", | ||
| "Plan": [ | ||
| "HashJoin 8000.00 root Null-aware anti semi join, left side:TableReader, equal:[eq(test.t1.a, test.t2.a)]", | ||
| "├─IndexReader(Build) 10000.00 root index:IndexFullScan", | ||
| "│ └─IndexFullScan 10000.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo", | ||
| "└─TableReader(Probe) 10000.00 root data:TableFullScan", | ||
| " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "3 3" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where exists (select 1 from t2 where t2.a > t1.a)", | ||
| "Plan": [ | ||
| "Apply 10000.00 root CARTESIAN semi join, left side:TableReader", | ||
| "├─TableReader(Build) 10000.00 root data:TableFullScan", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "└─Limit(Probe) 10000.00 root offset:0, count:1", | ||
| " └─IndexReader 10000.00 root index:Limit", | ||
| " └─Limit 10000.00 cop[tikv] offset:0, count:1", | ||
| " └─Selection 809900.00 cop[tikv] gt(test.t2.a, test.t1.a)", | ||
| " └─IndexFullScan 1012375.00 cop[tikv] table:t2, index:a(a) keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where exists (select 1 from t2 where t2.a = t1.a and t2.b > t1.b)", | ||
| "Plan": [ | ||
| "Apply 10000.00 root CARTESIAN semi join, left side:TableReader", | ||
| "├─TableReader(Build) 10000.00 root data:TableFullScan", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "└─Limit(Probe) 10000.00 root offset:0, count:1", | ||
| " └─IndexLookUp 10000.00 root ", | ||
| " ├─IndexRangeScan(Build) 13375.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo", | ||
| " └─Limit(Probe) 10000.00 cop[tikv] offset:0, count:1", | ||
| " └─Selection 10000.00 cop[tikv] gt(test.t2.b, test.t1.b)", | ||
| " └─TableRowIDScan 13375.00 cop[tikv] table:t2 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where exists (select /*+ NO_DECORRELATE() */ 1 from t2 where t2.a = t1.a)", | ||
| "Plan": [ | ||
| "Apply 10000.00 root CARTESIAN semi join, left side:TableReader", | ||
| "├─TableReader(Build) 10000.00 root data:TableFullScan", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", | ||
| "└─Limit(Probe) 10000.00 root offset:0, count:1", | ||
| " └─IndexReader 10000.00 root index:Limit", | ||
| " └─Limit 10000.00 cop[tikv] offset:0, count:1", | ||
| " └─IndexRangeScan 10000.00 cop[tikv] table:t2, index:a(a) range: decided by [eq(test.t2.a, test.t1.a)], keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where a in (select t2.a from t2 inner join t3 on t3.a = t2.b where t3.b > 0)", | ||
| "Plan": [ | ||
| "HashJoin 7992.00 root semi join, left side:TableReader, equal:[eq(test.t1.a, test.t2.a)]", | ||
| "├─HashJoin(Build) 4162.50 root inner join, equal:[eq(test.t3.a, test.t2.b)]", | ||
| "│ ├─TableReader(Build) 3330.00 root data:Selection", | ||
| "│ │ └─Selection 3330.00 cop[tikv] gt(test.t3.b, 0), not(isnull(test.t3.a))", | ||
| "│ │ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo", | ||
| "│ └─TableReader(Probe) 9980.01 root data:Selection", | ||
| "│ └─Selection 9980.01 cop[tikv] not(isnull(test.t2.a)), not(isnull(test.t2.b))", | ||
| "│ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", | ||
| "└─TableReader(Probe) 9990.00 root data:Selection", | ||
| " └─Selection 9990.00 cop[tikv] not(isnull(test.t1.a))", | ||
| " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| }, | ||
| { | ||
| "SQL": "select * from t1 where a in (select a from t2) order by a limit 10", | ||
| "Plan": [ | ||
| "Limit 10.00 root offset:0, count:10", | ||
| "└─MergeJoin 10.00 root semi join, left side:Projection, left key:test.t1.a, right key:test.t2.a", | ||
| " ├─IndexReader(Build) 12.50 root index:IndexFullScan", | ||
| " │ └─IndexFullScan 12.50 cop[tikv] table:t2, index:a(a) keep order:true, stats:pseudo", | ||
| " └─Projection(Probe) 12.50 root test.t1.a, test.t1.b", | ||
| " └─IndexLookUp 12.50 root ", | ||
| " ├─IndexFullScan(Build) 12.50 cop[tikv] table:t1, index:a(a) keep order:true, stats:pseudo", | ||
| " └─TableRowIDScan(Probe) 12.50 cop[tikv] table:t1 keep order:false, stats:pseudo" | ||
| ], | ||
| "Result": [ | ||
| "1 1", | ||
| "2 2" | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| ] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.