Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
350 changes: 350 additions & 0 deletions pkg/bindinfo/binding_auto_test.go

Large diffs are not rendered by default.

988 changes: 988 additions & 0 deletions pkg/bindinfo/binding_plan_generation.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions pkg/planner/core/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ go_library(
"rule_aggregation_elimination.go",
"rule_aggregation_push_down.go",
"rule_aggregation_skew_rewrite.go",
<<<<<<< HEAD
"rule_collect_plan_stats.go",
"rule_column_pruning.go",
=======
"rule_correlate.go",
>>>>>>> 7357a2e2f90 (planner: correlate subquery rule (#66206))
Comment on lines +56 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Resolve the leftover merge conflict in the srcs list.

The <<<<<<< / ======= / >>>>>>> markers are still present here, so this Bazel file will not parse and the cherry-pick cannot build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/BUILD.bazel` around lines 56 - 61, Remove the leftover merge
conflict markers (<<<<<<<, =======, >>>>>>>) in the BUILD.bazel srcs list and
reconcile the entries so the list contains the intended source files (include
"rule_collect_plan_stats.go", "rule_column_pruning.go" and "rule_correlate.go"
as appropriate), ensuring the srcs array is a valid, comma-separated string list
with no conflict markers remaining.

"rule_decorrelate.go",
"rule_derive_topn_from_window.go",
"rule_eliminate_projection.go",
Expand Down
10 changes: 10 additions & 0 deletions pkg/planner/core/casetest/rule/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ go_test(
timeout = "short",
srcs = [
"main_test.go",
<<<<<<< HEAD
=======
"rule_cdc_join_reorder_test.go",
"rule_common_handle_ordering_test.go",
"rule_correlate_test.go",
>>>>>>> 7357a2e2f90 (planner: correlate subquery rule (#66206))
Comment on lines +8 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Resolve the merge conflict markers before merging.

This target still has unresolved <<<<<<< / ======= / >>>>>>> blocks, so Bazel cannot load it and the rule tests will not build.

Also applies to: 24-28

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/casetest/rule/BUILD.bazel` around lines 8 - 13, The
BUILD.bazel target contains unresolved Git merge conflict markers (<<<<<<<,
=======, >>>>>>>) around the test file list; remove these markers and merge the
two variants into a single, valid list (e.g., include
"rule_cdc_join_reorder_test.go", "rule_common_handle_ordering_test.go",
"rule_correlate_test.go" along with the other files) so the target is a proper
Bazel srcs array and the rule tests (the files named in the conflict) can build.

"rule_derive_topn_from_window_test.go",
"rule_eliminate_projection_test.go",
"rule_inject_extra_projection_test.go",
Expand All @@ -15,7 +21,11 @@ go_test(
],
data = glob(["testdata/**"]),
flaky = True,
<<<<<<< HEAD
shard_count = 12,
=======
shard_count = 23,
>>>>>>> 7357a2e2f90 (planner: correlate subquery rule (#66206))
deps = [
"//pkg/config",
"//pkg/domain",
Expand Down
32 changes: 32 additions & 0 deletions pkg/planner/core/casetest/rule/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,24 @@ var testDataMap = make(testdata.BookKeeper)
func TestMain(m *testing.M) {
testsetup.SetupForCommonTest()
flag.Parse()
<<<<<<< HEAD
testDataMap.LoadTestSuiteData("testdata", "outer2inner")
testDataMap.LoadTestSuiteData("testdata", "derive_topn_from_window")
testDataMap.LoadTestSuiteData("testdata", "join_reorder_suite")
testDataMap.LoadTestSuiteData("testdata", "predicate_pushdown_suite")
testDataMap.LoadTestSuiteData("testdata", "predicate_simplification")
=======
testDataMap.LoadTestSuiteData("testdata", "outer2inner", true)
testDataMap.LoadTestSuiteData("testdata", "derive_topn_from_window", true)
testDataMap.LoadTestSuiteData("testdata", "join_reorder_suite", true)
testDataMap.LoadTestSuiteData("testdata", "predicate_pushdown_suite", true)
testDataMap.LoadTestSuiteData("testdata", "predicate_simplification", true)
testDataMap.LoadTestSuiteData("testdata", "outer_to_semi_join_suite", true)
testDataMap.LoadTestSuiteData("testdata", "correlate_suite", true)
testDataMap.LoadTestSuiteData("testdata", "cdc_join_reorder_suite", true)
testDataMap.LoadTestSuiteData("testdata", "order_aware_join_reorder_suite", true)

>>>>>>> 7357a2e2f90 (planner: correlate subquery rule (#66206))
Comment on lines +32 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Resolve the merge conflict markers in TestMain and the suite helpers.

The <<<<<<< / ======= / >>>>>>> markers are still in this Go file, so the test package will not compile.

Also applies to: 87-105

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/casetest/rule/main_test.go` around lines 32 - 49, Remove the
leftover merge conflict markers and fix TestMain to a single coherent block:
delete the <<<<<<<, =======, and >>>>>>> markers and keep the intended
LoadTestSuiteData calls (use the variant with the boolean flag, e.g.
LoadTestSuiteData("testdata", "outer2inner", true) and the additional suites
like "outer_to_semi_join_suite", "correlate_suite", "cdc_join_reorder_suite",
"order_aware_join_reorder_suite"); ensure the TestMain function and any suite
helper loops use the correct LoadTestSuiteData signature (the version accepting
the bool) and remove duplicated or conflicting lines so the file compiles.

opts := []goleak.Option{
goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"),
goleak.IgnoreTopFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1"),
Expand Down Expand Up @@ -71,3 +84,22 @@ func GetPredicatePushdownSuiteData() testdata.TestData {
func GetPredicateSimplificationSuiteData() testdata.TestData {
return testDataMap["predicate_simplification"]
}
<<<<<<< HEAD
=======

func GetOuterToSemiJoinSuiteData() testdata.TestData {
return testDataMap["outer_to_semi_join_suite"]
}

func GetCorrelateSuiteData() testdata.TestData {
return testDataMap["correlate_suite"]
}

func GetCDCJoinReorderSuiteData() testdata.TestData {
return testDataMap["cdc_join_reorder_suite"]
}

func GetOrderAwareJoinReorderSuiteData() testdata.TestData {
return testDataMap["order_aware_join_reorder_suite"]
}
>>>>>>> 7357a2e2f90 (planner: correlate subquery rule (#66206))
250 changes: 250 additions & 0 deletions pkg/planner/core/casetest/rule/rule_correlate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// Copyright 2026 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 (
"fmt"
"strings"
"testing"

"github.com/pingcap/tidb/pkg/testkit"
"github.com/pingcap/tidb/pkg/testkit/testdata"
"github.com/stretchr/testify/require"
)

// TestCorrelateNullSemantics verifies that CorrelateSolver does not break
// 3-valued NULL semantics for scalar IN (LeftOuterSemiJoin).
func TestCorrelateNullSemantics(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")

// Case 1: non-null outer, null inner → must return NULL (not 0).
tk.MustExec("drop table if exists tn, sn")
tk.MustExec("create table tn(a int)")
tk.MustExec("create table sn(a int, key(a))")
tk.MustExec("insert into tn values (1)")
tk.MustExec("insert into sn values (null)")
tk.MustQuery("select tn.a in (select sn.a from sn) as r from tn").Check(testkit.Rows("<nil>"))

// Case 2: null outer, non-null inner → must return NULL (not 0).
tk.MustExec("truncate table tn")
tk.MustExec("truncate table sn")
tk.MustExec("insert into tn values (null)")
tk.MustExec("insert into sn values (1)")
tk.MustQuery("select tn.a in (select sn.a from sn) as r from tn").Check(testkit.Rows("<nil>"))

// Case 3: both columns NOT NULL → correlate is safe; verify correct results.
tk.MustExec("drop table if exists tnn, snn")
tk.MustExec("create table tnn(a int not null)")
tk.MustExec("create table snn(a int not null, key(a))")
tk.MustExec("insert into tnn values (1), (2), (3)")
tk.MustExec("insert into snn values (1), (2)")
tk.MustQuery("select tnn.a in (select snn.a from snn) as r from tnn order by tnn.a").Check(testkit.Rows("1", "1", "0"))
}

// TestCorrelateAlternativeChoosesApply verifies that the correlate alternative
// round produces an Apply plan that wins the cost comparison for a non-correlated
// IN subquery when an outer WHERE predicate reduces the estimated row count.
// Without alternative plans, the InnerJoin+Agg rewrite produces IndexJoin+StreamAgg.
// With alternative plans, the correlate round produces Apply+Limit which is cheaper
// (avoids the StreamAgg overhead and uses Limit 1 for early exit on the inner side).
func TestCorrelateAlternativeChoosesApply(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1 (a int not null, b int, key(a))")
tk.MustExec("create table t2 (a int not null, 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)")

sql := "select * from t1 where b = 1 and a in (select a from t2)"

// Without alternative plans: standard InnerJoin+Agg path produces IndexJoin.
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = OFF")
rows := tk.MustQuery("explain format = 'brief' " + sql).Rows()
require.True(t, explainContains(rows, "IndexJoin"),
"without alternative plans, expected IndexJoin in plan:\n%s", joinExplainRows(rows))

// With alternative plans: correlate round produces Apply (cheaper than IndexJoin+StreamAgg).
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")
rows = tk.MustQuery("explain format = 'brief' " + sql).Rows()
require.True(t, explainContains(rows, "Apply"),
"with alternative plans, expected Apply in plan:\n%s", joinExplainRows(rows))

// Verify correct results in both modes.
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = OFF")
tk.MustQuery(sql).Check(testkit.Rows("1 1"))
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")
tk.MustQuery(sql).Check(testkit.Rows("1 1"))
}

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_alternative_logical_plans = 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...))
}
})
}

// explainContains scans all explain rows for a substring in the operator column.
func explainContains(rows [][]any, substr string) bool {
for _, row := range rows {
if strings.Contains(row[0].(string), substr) {
return true
}
}
return false
}

// joinExplainRows formats explain rows into a single string for debug output.
func joinExplainRows(rows [][]any) string {
var sb strings.Builder
for _, row := range rows {
sb.WriteString(row[0].(string))
sb.WriteByte('\n')
}
return sb.String()
}

// TestCorrelateParallelApply verifies that when the correlate alternative round
// produces an Apply plan and tidb_enable_parallel_apply is ON, the Apply is
// executed with parallel concurrency. This tests the interaction between the
// correlate optimization (converting decorrelated semi-join back to Apply) and
// the parallel apply executor.
func TestCorrelateParallelApply(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1 (a int not null, b int, key(a))")
tk.MustExec("create table t2 (a int not null, b int, key(a))")
tk.MustExec("insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5)")
tk.MustExec("insert into t2 values (1,10),(2,20),(3,30)")

sql := "select * from t1 where b = 1 and a in (select a from t2)"

// Enable correlate alternative + parallel apply.
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")
tk.MustExec("set tidb_enable_parallel_apply = ON")
tk.MustExec("set tidb_executor_concurrency = 5")

// Verify the plan contains Apply (correlate alternative won).
rows := tk.MustQuery("explain format = 'brief' " + sql).Rows()
require.True(t, explainContains(rows, "Apply"),
"with correlate alternative + parallel apply, expected Apply in plan:\n%s", joinExplainRows(rows))

// Verify EXPLAIN ANALYZE reports Concurrency > 1 for the Apply.
analyzeRows := tk.MustQuery("explain analyze " + sql).Rows()
foundConcurrency := false
for _, row := range analyzeRows {
line := fmt.Sprintf("%v", row)
if strings.Contains(line, "Apply") && strings.Contains(line, "Concurrency:") {
idx := strings.Index(line, "Concurrency:")
if idx >= 0 {
rest := line[idx+len("Concurrency:"):]
var n int
if _, err := fmt.Sscanf(rest, "%d", &n); err == nil && n > 1 {
foundConcurrency = true
}
}
break
}
}
require.True(t, foundConcurrency,
"EXPLAIN ANALYZE must report Concurrency > 1 for Apply when parallel_apply is on")
Comment on lines +167 to +197
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use multiple qualifying outer rows before asserting Concurrency > 1.

This fixture leaves only one outer row after b = 1, so the Apply can legitimately execute with effective concurrency 1. That makes the EXPLAIN ANALYZE assertion flaky even if parallel apply is working. Either add more t1 rows that satisfy the outer predicate or use a less selective query for the concurrency check. As per coding guidelines "Test files: Prefer extending existing test suites and fixtures over creating new scaffolding; keep test changes minimal and deterministic."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/casetest/rule/rule_correlate_test.go` around lines 167 -
197, The test currently only yields one qualifying outer row, making the
Concurrency>1 check flaky; before running the explains, ensure multiple outer
rows qualify by inserting additional rows into t1 (e.g. via tk.MustExec("insert
into t1 ...") or similar) or by relaxing the query predicate (modify sql to use
a less selective condition such as b IN (...)); update the setup immediately
before the explain/analyze block (affecting the sql variable and the
analyzeRows/foundConcurrency check) so the Apply operator has multiple outer
rows and the EXPLAIN ANALYZE concurrency assertion becomes deterministic.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@minodisk このコメントは別のプロジェクト(dutify/grannel の Slack インタラクションハンドラ)についての内容のようですが、こちらのレビューコメントは TiDB の pkg/planner/core/casetest/rule/rule_correlate_test.go にある TestCorrelateParallelApply テストについてのものです。


My original concern still stands: in TestCorrelateParallelApply, the data setup inserts rows (1,1),(2,2),(3,3),(4,4),(5,5) into t1, but the query uses b = 1, which means only one outer row (a=1, b=1) matches. With a single outer row, parallel apply may legitimately run with effective concurrency of 1, making the Concurrency > 1 assertion unreliable.

To make the test deterministic, consider inserting multiple rows into t1 that satisfy b = 1 and also exist in t2, for example:

// Replace current t1 inserts with rows that give multiple qualifying outer rows:
tk.MustExec("insert into t1 values (1,1),(2,1),(3,1),(4,4),(5,5)")
// Now b=1 matches (1,1),(2,1),(3,1) and t2 has (1,10),(2,20),(3,30),
// so 3 outer rows feed into the Apply, making Concurrency>1 meaningful.

Or alternatively, relax the query predicate so more than one outer row qualifies before the subquery evaluation.


🧠 Learnings used
Learnt from: joechenrh
Repo: pingcap/tidb PR: 66878
File: pkg/planner/core/logical_plan_builder.go:6455-6472
Timestamp: 2026-03-11T06:29:00.122Z
Learning: Ensure code reviews verify the VirtualAssignmentsOffset semantics: the planner sets Update.VirtualAssignmentsOffset = len(update.List). The executor should only apply OrderedList[:VirtualAssignmentsOffset] when composing new rows, and only after the 'changed' check should it group/evaluate OrderedList[VirtualAssignmentsOffset:] per table. This pattern applies to files under pkg/planner/core and pkg/executor (e.g., common_plans.go and update.go). Reviewers should check that updates respect slicing behavior, that the offset is consistently derived from the planner, and that downstream code does not bypass the offset when creating new rows. Add tests validating both branches: the slice before the offset for new rows, and the per-table handling of the slice after the offset.

Learnt from: joechenrh
Repo: pingcap/tidb PR: 64752
File: pkg/planner/core/planbuilder.go:3987-3991
Timestamp: 2026-03-12T02:21:56.529Z
Learning: Do not change guards that rely on onDups being nil vs non-nil. In resolveGeneratedColumns, nil onDups means IMPORT INTO/LOAD DATA path (do not mutate the map and ON UPDATE NOW columns should not be recorded). Non-nil (even if empty) means the INSERT path, where ON UPDATE NOW columns should be added to onDups to include in igc.OnDuplicates. The check should remain onDups != nil; do not substitute len(onDups) > 0. This nil/non-nil contract was established by PR `#63146` to fix a panic on IMPORT INTO. When reviewing similar code, ensure the guard matches the documented contract and update tests accordingly.

Learnt from: winoros
Repo: pingcap/tidb PR: 67411
File: pkg/planner/core/plan_cache_utils.go:0-0
Timestamp: 2026-04-01T11:13:19.535Z
Learning: In TiDB planner code, when `RequestLoadStats` or `SyncWaitStatsLoad` fails specifically under `vardef.StatsLoadPseudoTimeout` (sync-load timeout path), ensure the statement context disables plan-cache writes for the sync-load fallback by calling `stmtCtx.SetSkipPlanCache(skipPlanCacheReasonSyncLoadFallback)` immediately. This prevents the fallback plan from being stored in the plan cache (and re-hit later with stale/pseudo stats). Do not propose an approach that caches the fallback plan and later invalidates it (e.g., via `SyncLoadFallbackItems` or similar), since that leaves a stale entry resident in the cache.

Learnt from: takaidohigasi
Repo: pingcap/tidb PR: 68049
File: pkg/planner/core/find_best_task.go:0-0
Timestamp: 2026-04-25T14:31:42.563Z
Learning: When reviewing TiDB planner logic for matching index properties (e.g., PK/CommonHandle suffix handling in `matchProperty`), do not flag a hard-coded assumption that the PK suffix columns are ASC (`thisIdxDesc := false`) as incorrect as long as DDL time guards prevent creating PK suffix columns with `Desc=true` on clustered primary key columns. If/when the DDL enforcement is relaxed to allow mixed-direction PK encoding (or similar support is added, e.g., via `CommonHandleRangesToKVRanges`), update the planner code to derive each PK suffix column’s Desc/ordering from the primary index metadata (instead of hard-coding ASC) so mixed-direction PK encodings are matched correctly.

Learnt from: takaidohigasi
Repo: pingcap/tidb PR: 68049
File: pkg/planner/core/planbuilder.go:1288-1297
Timestamp: 2026-04-26T07:19:00.632Z
Learning: In pkg/planner/core DML plan building (e.g., INSERT/REPLACE, LOAD DATA, IMPORT INTO, and similarly UPDATE/DELETE), apply the “servability fence” checkAllIndicesServable early in the builder before any mutation paths run. The checkAllIndicesServable logic should mirror tables.IsIndexWritable: it should skip only StateDeleteOnly and StateDeleteReorganization, and it must error on any other writable index whose metadata version is newer than what the binary supports. This ensures the planner fails fast when the target indexes aren’t servable by the current binary, preventing writes down mutation paths that would later hit unsupported index metadata.


// Verify correctness: parallel + correlate must match serial + no correlate.
tk.MustExec("set tidb_enable_parallel_apply = OFF")
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = OFF")
serialRows := tk.MustQuery(sql).Rows()

tk.MustExec("set tidb_enable_parallel_apply = ON")
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")
parallelRows := tk.MustQuery(sql).Rows()

require.Equal(t, serialRows, parallelRows,
"correlate alternative + parallel apply must produce the same result as standard path")
}

// TestCorrelateWithCostFactors verifies that when hash/merge join cost factors
// are increased, the correlate alternative round wins and produces Apply-based
// plans with correlated index access for cases that normally choose HashJoin.
func TestCorrelateWithCostFactors(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 and penalize hash/merge joins so the
// correlate alternative (Apply with index lookup) wins the cost comparison.
tk.MustExec("set tidb_opt_enable_alternative_logical_plans = ON")
tk.MustExec("set tidb_opt_hash_join_cost_factor = 1000")
tk.MustExec("set tidb_opt_merge_join_cost_factor = 1000")

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...))
}
})
}
39 changes: 39 additions & 0 deletions pkg/planner/core/casetest/rule/testdata/correlate_suite_in.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"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",
"select * from t1 where a in (select a from t2 where b > 1)",
"select * from t1 where a in (select a from t2 group by a)",
"select * from t1 where a in (select a from t2 where b > 1 group by a)",
"select * from t1 where a in (select a from t2 limit 10)",
"select * from t1 where a in (select a from t2 order by a limit 10)",
"select * from t1 where b = 1 and a in (select a from t2)",
"select * from t1 where b = 1 and exists (select 1 from t2 where t2.a = t1.a) limit 1",
"select * from t1 where b = 1 and a not in (select a from t2) limit 1",
"select * from t1 where b = 1 and a in (select a from t2 where t2.b > 0) limit 1"
]
},
{
"name": "TestCorrelateWithCostFactors",
"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 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 a in (select a from t2) order by a limit 10",
"select * from t1 where a in (select a from t2 where b > 1)",
"select * from t1 where a in (select a from t2 order by a limit 10)"
]
}
]
Loading