planner: enable cd-c algorithm by default for join reorder | tidb-test=pr/2676 (#66349)#67289
planner: enable cd-c algorithm by default for join reorder | tidb-test=pr/2676 (#66349)#67289ti-chi-bot wants to merge 1 commit intopingcap:release-7.5from
Conversation
Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
|
This cherry pick PR is for a release branch and has not yet been approved by triage owners. To merge this cherry pick:
DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
@guo-shaoge This PR has conflicts, I have hold it. |
|
@ti-chi-bot: ## If you want to know how to resolve it, please read the guide in TiDB Dev Guide. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository. |
📝 WalkthroughWalkthroughThis PR implements a new join order optimizer based on the CD-C (Conflict Detector with Conflict Rules) algorithm for handling outer, semi, and anti joins. It adds a new Changes
Sequence Diagram(s)sequenceDiagram
actor Planner
participant JoinReorderRule as Join Reorder Rule
participant JoinOrder as joinorder.Optimize()
participant ConflictDetector as Conflict Detector
participant JoinGroup as Join Group
participant EdgeValidator as Edge Validator
participant TreeBuilder as Tree Builder
Planner->>JoinReorderRule: Optimize(plan)
alt Advanced Reorder Enabled
JoinReorderRule->>JoinOrder: Optimize(plan)
JoinOrder->>ConflictDetector: Build(joinGroup)
ConflictDetector->>JoinGroup: Extract vertexes, hints, edges
ConflictDetector->>EdgeValidator: Compute conflict rules for edges
JoinOrder->>TreeBuilder: optimizeForJoinGroup()
TreeBuilder->>TreeBuilder: Greedy join enumeration
TreeBuilder->>EdgeValidator: CheckConnection(node1, node2)
EdgeValidator-->>TreeBuilder: Valid connection + rules satisfied?
TreeBuilder->>ConflictDetector: MakeJoin(checkResult)
ConflictDetector-->>TreeBuilder: New reordered join node
TreeBuilder->>TreeBuilder: Build remaining bushy tree
TreeBuilder-->>JoinOrder: Optimized join tree
JoinOrder-->>JoinReorderRule: Reordered plan
else Standard Reorder
JoinReorderRule->>JoinReorderRule: optimizeRecursive(plan)
end
JoinReorderRule-->>Planner: Optimized plan
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.11.3)Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_out.json (1)
1208-1340: Add aleading()case that explicitly requires a cartesian edge.
TestJoinReorderPushSelectionexercisesleading(...), but none of these cases pins the special exception where a hint is allowed to force a cartesian join. That edge regressed before, so I’d add one positive hinted-cartesian case and one comparable non-hinted case to keep the gating behavior locked down.Based on learnings: "In
pkg/planner/core/joinorder/(TiDB join reorder), cartesian edges should only be considered in two specific scenarios: 1. After the entire join reorder is complete and there are still disjoint join groups that need to be connected. 2. When aleadinghint explicitly requires a cartesian join."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_out.json` around lines 1208 - 1340, Add two cases to the TestJoinReorderPushSelection suite: (1) a SQL with a leading(...) hint that forces a cartesian edge (so the expected Plan shows a cartesian join/connect between disjoint join groups) and (2) a comparable SQL without the leading hint that must NOT produce that cartesian edge (expected Plan should show normal reorder without cartesian). Locate the JSON array under "Cases" for TestJoinReorderPushSelection and add the two entries with unique SQL strings and matching "Plan" arrays; ensure the hinted case references the same table groups as the non-hinted case and that the expected plan text explicitly indicates the cartesian/connect behavior so the test locks the special-case allowing cartesian only when leading(...) forces it.pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_xut.json (1)
3-5: Add a regression for cartesian-edge gating.This suite still misses the case that previously regressed: disconnected join subgraphs with no
leading(...)should only get a cartesian join in the final stitch, while aleading(...)case may require one earlier. Please lock that down here.Based on learnings: planner rule changes should add targeted planner tests, and in
pkg/planner/core/joinorder/cartesian edges should only be considered after the entire join reorder is complete with disjoint groups, or when aleadinghint explicitly requires a cartesian join.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_xut.json` around lines 3 - 5, Add a targeted regression case to the TestCDCJoinReorder suite that covers disconnected join subgraphs: create a test input where two disjoint join groups are present with no leading(...) hints and assert that a cartesian edge is only introduced in the final stitch (not during intermediate reorder steps); also add a companion case where a leading(...) hint forces an earlier cartesian join so the planner emits the cartesian edge earlier. Ensure the new cases exercise the planner join reorder logic (joinorder/cartesian-edge handling) and assert the presence/absence and position of cartesian joins accordingly.pkg/planner/core/casetest/testdata/json_plan_suite_xut.json (1)
101-162: Add one outer/semi/anti-jointidb_jsoncase here.The new
explain analyze format = tidb_jsoncoverage only exercises a plain inner join. Since this PR turns cd-c on by default for outer/semi/anti reorder, one golden from that family would make this suite hit the changed planner path instead of only a stable inner-join plan.Based on learnings: For planner rule or logical/physical plan changes, perform targeted planner unit tests and update rule testdata when needed.
Also applies to: 272-333
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/planner/core/casetest/testdata/json_plan_suite_xut.json` around lines 101 - 162, Add a new test case object alongside the existing one that uses "SQL": "explain analyze format = tidb_json ..." with an outer/semi/anti join (e.g., a LEFT JOIN like "select * from t1 left join t2 on t1.id = t2.id" or a SEMI/ANTI variant) so the suite exercises the CD-C outer/semi/anti reorder path; place it as another top-level entry with its own "JSONPlan" stub/expected plan (mirroring structure of the existing "JSONPlan" key) and ensure a corresponding expected operator id (e.g., an outer-join operator id or semi/anti join id) is present so the planner unit test will hit the changed planner path—repeat the same addition in the other suite block noted by the reviewer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/planner/core/casetest/testdata/json_plan_suite_xut.json`:
- Around line 1-4: The new json_plan_suite_xut.json fixture is not picked up
because LoadTestSuiteData in pkg/testkit/testdata/testdata.go only looks for
*_in.json/*_out.json pairs; either rename json_plan_suite_xut.json to follow the
existing *_in.json/*_out.json convention so it will be discovered, or update
LoadTestSuiteData (and any helper functions/matchers it uses) to also recognize
the _xut.json suffix by adding that pattern to the file discovery logic and
ensure it is parsed/paired the same way as the existing in/out files.
In `@pkg/planner/core/joinorder/join_order.go`:
- Around line 454-473: The greedy pass currently conflates allowing non-equi
joins and permitting cartesian stitching via allowNoEQ; change the API and call
sites so greedyConnectJoinNodes only controls non-equi edge usage (rename/keep
allowNonEq), and add a separate boolean or mode (e.g., allowCartesian) that is
NOT set in the normal greedy passes. Remove any calls inside
greedyConnectJoinNodes that invoke TryCreateCartesianCheckResult when
allowCartesian is false. Ensure cartesian creation is only invoked from
buildJoinByHint (when a leading hint explicitly requests it) and from the final
fallback (makeBushyTree) after the whole reorder if
detector.HasRemainingEdges(usedEdges) still indicates disconnected groups; keep
the cartesianFactor logic and clamping only in those cartesian-specific paths
and do not enable cartesian stitching in the intermediate retry where you only
want to enable non-equi joins (the second greedyConnectJoinNodes call should
pass allowCartesian=false but allowNonEq=true).
In `@tests/integrationtest/r/planner/core/casetest/index/index.result`:
- Around line 1-823: Missing pairing for the integration test result:
tests/integrationtest/r/planner/core/casetest/index/index.result has no
corresponding test input file; either add
tests/integrationtest/t/planner/core/casetest/index/index.test containing the
test SQLs that produced this result or confirm that the result should be merged
into an existing file such as
tests/integrationtest/t/planner/core/casetest/integration.test, then regenerate
the .result by running the framework's recording command so the result and test
file are in sync.
In `@tests/integrationtest/r/planner/core/issuetest/planner_issue.result`:
- Around line 668-679: The test's non-deterministic LIMIT usage (the SQL "select
* from t t1 left join t t2 on t1.a=t2.a limit 1, 1" and its corresponding
"explain format='plan_tree' ... limit 1, 1") must include an explicit ORDER BY
to guarantee a stable returned row; update both the query and the EXPLAIN query
to add a deterministic ordering key (for example "ORDER BY t1.a" or a stable
primary key/row id) so the expected row assertion is deterministic and matches
the recorded plan output.
---
Nitpick comments:
In `@pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_out.json`:
- Around line 1208-1340: Add two cases to the TestJoinReorderPushSelection
suite: (1) a SQL with a leading(...) hint that forces a cartesian edge (so the
expected Plan shows a cartesian join/connect between disjoint join groups) and
(2) a comparable SQL without the leading hint that must NOT produce that
cartesian edge (expected Plan should show normal reorder without cartesian).
Locate the JSON array under "Cases" for TestJoinReorderPushSelection and add the
two entries with unique SQL strings and matching "Plan" arrays; ensure the
hinted case references the same table groups as the non-hinted case and that the
expected plan text explicitly indicates the cartesian/connect behavior so the
test locks the special-case allowing cartesian only when leading(...) forces it.
In `@pkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_xut.json`:
- Around line 3-5: Add a targeted regression case to the TestCDCJoinReorder
suite that covers disconnected join subgraphs: create a test input where two
disjoint join groups are present with no leading(...) hints and assert that a
cartesian edge is only introduced in the final stitch (not during intermediate
reorder steps); also add a companion case where a leading(...) hint forces an
earlier cartesian join so the planner emits the cartesian edge earlier. Ensure
the new cases exercise the planner join reorder logic (joinorder/cartesian-edge
handling) and assert the presence/absence and position of cartesian joins
accordingly.
In `@pkg/planner/core/casetest/testdata/json_plan_suite_xut.json`:
- Around line 101-162: Add a new test case object alongside the existing one
that uses "SQL": "explain analyze format = tidb_json ..." with an
outer/semi/anti join (e.g., a LEFT JOIN like "select * from t1 left join t2 on
t1.id = t2.id" or a SEMI/ANTI variant) so the suite exercises the CD-C
outer/semi/anti reorder path; place it as another top-level entry with its own
"JSONPlan" stub/expected plan (mirroring structure of the existing "JSONPlan"
key) and ensure a corresponding expected operator id (e.g., an outer-join
operator id or semi/anti join id) is present so the planner unit test will hit
the changed planner path—repeat the same addition in the other suite block noted
by the reviewer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 915dd777-f94c-43ec-8892-9dc6ff0427fa
📒 Files selected for processing (34)
pkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_out.jsonpkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_xut.jsonpkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.jsonpkg/planner/core/casetest/cbotest/testdata/analyze_suite_xut.jsonpkg/planner/core/casetest/hint/testdata/integration_suite_out.jsonpkg/planner/core/casetest/hint/testdata/integration_suite_xut.jsonpkg/planner/core/casetest/mpp/testdata/integration_suite_out.jsonpkg/planner/core/casetest/mpp/testdata/integration_suite_xut.jsonpkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_out.jsonpkg/planner/core/casetest/rule/testdata/cdc_join_reorder_suite_xut.jsonpkg/planner/core/casetest/rule/testdata/join_reorder_suite_out.jsonpkg/planner/core/casetest/rule/testdata/join_reorder_suite_xut.jsonpkg/planner/core/casetest/rule/testdata/outer2inner_out.jsonpkg/planner/core/casetest/rule/testdata/outer2inner_xut.jsonpkg/planner/core/casetest/rule/testdata/outer_to_semi_join_suite_out.jsonpkg/planner/core/casetest/rule/testdata/outer_to_semi_join_suite_xut.jsonpkg/planner/core/casetest/testdata/json_plan_suite_out.jsonpkg/planner/core/casetest/testdata/json_plan_suite_xut.jsonpkg/planner/core/casetest/tpcds/testdata/tpcds_suite_out.jsonpkg/planner/core/casetest/tpcds/testdata/tpcds_suite_xut.jsonpkg/planner/core/casetest/tpch/testdata/tpch_suite_out.jsonpkg/planner/core/casetest/tpch/testdata/tpch_suite_xut.jsonpkg/planner/core/joinorder/BUILD.bazelpkg/planner/core/joinorder/conflict_detector.gopkg/planner/core/joinorder/join_order.gopkg/planner/core/rule_join_reorder.gotests/integrationtest/r/planner/core/casetest/index/index.resulttests/integrationtest/r/planner/core/casetest/rule/rule_join_reorder.resulttests/integrationtest/r/planner/core/issuetest/planner_issue.resulttests/integrationtest/r/planner/core/join_reorder2.resulttests/integrationtest/r/planner/core/plan_cache.resulttests/integrationtest/r/planner/core/rule_join_reorder.resulttests/integrationtest/r/select.resulttests/integrationtest/r/tpch.result
| [ | ||
| { | ||
| "Name": "TestJSONPlanInExplain", | ||
| "Cases": [ |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "New xut fixtures under pkg/planner:"
fd '_xut\.json$' pkg/planner
echo
echo "Direct references to the new suffix or exact filenames:"
rg -n --glob '*.go' 'json_plan_suite_xut|binary_plan_suite_xut|_xut\.json|xut'
echo
echo "Planner casetest fixture-loading code paths:"
rg -n --glob '*.go' 'testdata|_out\.json|Load.*json|Read.*json' pkg/plannerRepository: pingcap/tidb
Length of output: 877
🏁 Script executed:
# Find the test runner/loader code in casetest packages
find pkg/planner/core/casetest -name "*test.go" -type f | head -5
# Look for the main test file that loads these fixtures
find pkg/planner/core/casetest -name "casetest_test.go" -o -name "*_test.go" | grep -E "(json_plan|casetest)" | head -10Repository: pingcap/tidb
Length of output: 809
🏁 Script executed:
# Search for file discovery patterns and glob expressions in casetest
rg -n -A5 -B5 'json_plan_suite|testdata' pkg/planner/core/casetest --glob '*.go' | head -100Repository: pingcap/tidb
Length of output: 9057
🏁 Script executed:
# Check the json plan test file specifically
find pkg/planner/core/casetest -name "*json*" -type f | grep -E '\.go$'Repository: pingcap/tidb
Length of output: 38
🏁 Script executed:
# Look for any references to file patterns like *_out.json or fixture loading
rg -n '\*_out\.json|\*\.json|filepath.Glob|ioutil.ReadDir' pkg/planner/core/casetest --glob '*.go' -A2Repository: pingcap/tidb
Length of output: 38
🏁 Script executed:
# Find the json plan test file and its suite definition
find pkg/planner/core/casetest -name "*json_plan*" -type fRepository: pingcap/tidb
Length of output: 234
🏁 Script executed:
# Search for GetJSONPlanSuiteData or similar suite getters
rg -n "GetJSONPlanSuiteData|JSONPlanSuite|json_plan" pkg/planner/core/casetest --glob '*.go'Repository: pingcap/tidb
Length of output: 414
🏁 Script executed:
# Look for suite_test.go files that typically define loaders
find pkg/planner/core/casetest -name "suite_test.go" -o -name "*suite*.go" | grep -v '.json'Repository: pingcap/tidb
Length of output: 38
🏁 Script executed:
# Examine main_test.go to see how LoadTestSuiteData works and understand the loader pattern
cat -n pkg/planner/core/casetest/main_test.go | head -100Repository: pingcap/tidb
Length of output: 2772
🏁 Script executed:
# Check plan_test.go to see how it uses the suite data
sed -n '250,270p' pkg/planner/core/casetest/plan_test.goRepository: pingcap/tidb
Length of output: 853
🏁 Script executed:
# Search for LoadTestSuiteData implementation
rg -n "LoadTestSuiteData" --glob '*.go' -A10 | head -80Repository: pingcap/tidb
Length of output: 6247
🏁 Script executed:
# Look at the loadTestSuiteData implementation
sed -n '100,250p' pkg/testkit/testdata/testdata.goRepository: pingcap/tidb
Length of output: 4558
🏁 Script executed:
# Also search for how the suite data discovery works - looking for file pattern matching
rg -n "loadTestSuiteData|_in\.json|_out\.json|_xut\.json" pkg/testkit/testdata --glob '*.go' -B2 -A5 | head -100Repository: pingcap/tidb
Length of output: 2391
The _xut.json files are not loaded by the test framework and will not execute.
The testdata loader in pkg/testkit/testdata/testdata.go (lines 59–70) only discovers *_in.json and *_out.json file pairs via LoadTestSuiteData(dir, suiteName). It does not load _xut.json files. The new json_plan_suite_xut.json fixture exists on disk but is orphaned and will never run. Either rename the files to match the expected _in.json/_out.json naming convention or extend the loader to support the _xut.json suffix.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/planner/core/casetest/testdata/json_plan_suite_xut.json` around lines 1 -
4, The new json_plan_suite_xut.json fixture is not picked up because
LoadTestSuiteData in pkg/testkit/testdata/testdata.go only looks for
*_in.json/*_out.json pairs; either rename json_plan_suite_xut.json to follow the
existing *_in.json/*_out.json convention so it will be discovered, or update
LoadTestSuiteData (and any helper functions/matchers it uses) to also recognize
the _xut.json suffix by adding that pattern to the file discovery logic and
ensure it is parsed/paired the same way as the existing in/out files.
| var cartesianFactor float64 = j.ctx.GetSessionVars().CartesianJoinOrderThreshold | ||
| var disableCartesian = cartesianFactor <= 0 | ||
| allowNoEQ := !disableCartesian && j.group.allInnerJoin | ||
| if nodes, err = greedyConnectJoinNodes(detector, nodes, j.group.vertexHints, cartesianFactor, allowNoEQ); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| usedEdges := collectUsedEdges(nodes) | ||
| if !allowNoEQ && detector.HasRemainingEdges(usedEdges) { | ||
| // After the first round of greedy connection, there are still some remaining edges, | ||
| // for example: R1 INNER JOIN R2 ON R1.c1 < R2.c2 | ||
| // the above join can only be connected when non-eq edges are allowed, | ||
| // and the first round of greedy enumeration is not allowed to use non-eq edges. | ||
| // So we got here and we need to the second round of enumeration with `allowNoEQ` as true. | ||
| befLen := len(nodes) | ||
| // Clamp to 1 to avoid cumCost*0=0 making non-EQ joins appear free. | ||
| if cartesianFactor <= 0 { | ||
| cartesianFactor = 1 | ||
| } | ||
| if nodes, err = greedyConnectJoinNodes(detector, nodes, j.group.vertexHints, cartesianFactor, true); err != nil { |
There was a problem hiding this comment.
Keep cartesian joins out of the greedy passes.
allowNoEQ is doing double duty here: once it is true, greedyConnectJoinNodes() can both accept non-equi edges and call TryCreateCartesianCheckResult() on disconnected subgraphs. That lets the main pass stitch disjoint groups together early, and the retry path does the same even when CartesianJoinOrderThreshold <= 0. Please split those controls and reserve cartesian creation for buildJoinByHint() and the final makeBushyTree() fallback.
Based on learnings: in pkg/planner/core/joinorder/, cartesian edges should only be considered in two specific scenarios — after the entire join reorder is complete and there are still disjoint join groups that need to be connected, or when a leading hint explicitly requires a cartesian join.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/planner/core/joinorder/join_order.go` around lines 454 - 473, The greedy
pass currently conflates allowing non-equi joins and permitting cartesian
stitching via allowNoEQ; change the API and call sites so greedyConnectJoinNodes
only controls non-equi edge usage (rename/keep allowNonEq), and add a separate
boolean or mode (e.g., allowCartesian) that is NOT set in the normal greedy
passes. Remove any calls inside greedyConnectJoinNodes that invoke
TryCreateCartesianCheckResult when allowCartesian is false. Ensure cartesian
creation is only invoked from buildJoinByHint (when a leading hint explicitly
requests it) and from the final fallback (makeBushyTree) after the whole reorder
if detector.HasRemainingEdges(usedEdges) still indicates disconnected groups;
keep the cartesianFactor logic and clamping only in those cartesian-specific
paths and do not enable cartesian stitching in the intermediate retry where you
only want to enable non-equi joins (the second greedyConnectJoinNodes call
should pass allowCartesian=false but allowNonEq=true).
| drop table if exists t1, t2; | ||
| set tidb_enable_clustered_index=int_only; | ||
| create table t1(a int not null, c int not null); | ||
| create table t2(a int not null, b int not null, c int not null, primary key(a,b)); | ||
| insert into t1 values(1,1); | ||
| insert into t2 values(1,1,1),(1,2,1); | ||
| analyze table t1,t2 all columns; | ||
| explain format='plan_tree' select /*+ TIDB_INLJ(t2) */ * from t1 join t2 on t1.a = t2.a and t1.c = t2.c; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t1.a, inner key:planner__core__casetest__index__index.t2.a, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a), eq(planner__core__casetest__index__index.t1.c, planner__core__casetest__index__index.t2.c) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─IndexLookUp(Probe) root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:PRIMARY(a, b) range: decided by [eq(planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t1.a)], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false | ||
| explain format='plan_tree' select /*+ TIDB_INLJ(t2) */ * from t1 join t2 on t1.a = t2.a and t1.c <= t2.b; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t1.a, inner key:planner__core__casetest__index__index.t2.a, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a), other cond:le(planner__core__casetest__index__index.t1.c, planner__core__casetest__index__index.t2.b) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─IndexLookUp(Probe) root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:PRIMARY(a, b) range: decided by [eq(planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t1.a) le(planner__core__casetest__index__index.t1.c, planner__core__casetest__index__index.t2.b)], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false | ||
| explain format='plan_tree' select /*+ TIDB_INLJ(t2) */ * from t1 join t2 on t1.a = t2.a and t2.b = 1; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t1.a, inner key:planner__core__casetest__index__index.t2.a, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─IndexLookUp(Probe) root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:PRIMARY(a, b) range: decided by [eq(planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t1.a) eq(planner__core__casetest__index__index.t2.b, 1)], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false | ||
| set tidb_enable_clustered_index=default; | ||
| drop table if exists t2; | ||
| create table t2(pk int primary key, a json, b json, c int, d int, e int, index idx(c, (cast(a as signed array))), index idx2((cast(b as signed array)), c), index idx3(c, d), index idx4(d)); | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where (1 member of (a) and c=1) or (2 member of (b) and c=1); -- 1: OR index merge from multi complicated mv index (memberof); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where (1 member of (a) and c=1) or (2 member of (b) and c=1); -- 2: OR index merge from multi complicated mv index (memberof); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where (1 member of (a) and c=1 and d=2) or (2 member of (b) and c=3 and d=2); -- 3: OR index merge from multi complicated mv index (memberof),while each DNF item contains redundant condition, which should be remained as table filters; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 3,2 3], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.a), and(eq(planner__core__casetest__index__index.t2.c, 1), eq(planner__core__casetest__index__index.t2.d, 2))), and(json_memberof(cast(2, json BINARY), planner__core__casetest__index__index.t2.b), and(eq(planner__core__casetest__index__index.t2.c, 3), eq(planner__core__casetest__index__index.t2.d, 2)))) | ||
| └─TableRowIDScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where ( json_contains(a, '[1, 2, 3]') and c=1 and d=2) or (2 member of (b) and c=3 and d=2); -- 4: OR index merge from multi complicated mv index (memberof),make full use of DNF item's condition even if the predicate is intersection case (json_contains); | ||
| id task access object operator info | ||
| IndexLookUp root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx3(c, d) range:[1 2,1 2], [3 2,3 2], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(json_contains(planner__core__casetest__index__index.t2.a, cast("[1, 2, 3]", json BINARY)), and(eq(planner__core__casetest__index__index.t2.c, 1), eq(planner__core__casetest__index__index.t2.d, 2))), and(json_memberof(cast(2, json BINARY), planner__core__casetest__index__index.t2.b), and(eq(planner__core__casetest__index__index.t2.c, 3), eq(planner__core__casetest__index__index.t2.d, 2)))) | ||
| └─TableRowIDScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where ( json_overlaps(a, '[1, 2, 3]') and c=1 and d=2) or (2 member of (b) and c=3 and d=2); -- 5: OR index merge from multi complicated mv index (memberof),make full use of DNF item's condition even if the predicate is intersection case (json_contains); | ||
| id task access object operator info | ||
| Selection root or(and(json_overlaps(planner__core__casetest__index__index.t2.a, cast("[1, 2, 3]", json BINARY)), and(eq(planner__core__casetest__index__index.t2.c, 1), eq(planner__core__casetest__index__index.t2.d, 2))), and(json_memberof(cast(2, json BINARY), planner__core__casetest__index__index.t2.b), and(eq(planner__core__casetest__index__index.t2.c, 3), eq(planner__core__casetest__index__index.t2.d, 2)))) | ||
| └─IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 2,1 2], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 3,1 3], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 3,2 3], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx, idx4) */ * from t2 where ( json_contains(a, '[1, 2, 3]') and d=2) or (2 member of (b) and c=3 and d=2); -- 6: OR index merge from multi complicated mv index (memberof),make full use of other DNF items even if one of the DNF items fails; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx4(d) range:[2,2], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 3,2 3], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(json_contains(planner__core__casetest__index__index.t2.a, cast("[1, 2, 3]", json BINARY)), eq(planner__core__casetest__index__index.t2.d, 2)), and(json_memberof(cast(2, json BINARY), planner__core__casetest__index__index.t2.b), and(eq(planner__core__casetest__index__index.t2.c, 3), eq(planner__core__casetest__index__index.t2.d, 2)))) | ||
| └─TableRowIDScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where (1 member of (a) and 1 member of (b) and c=3) or (3 member of (b) and c=4); -- 7: OR index merge from multi complicated mv index (memberof),each DNF item can be more complicated like a another embedded CNF member-of composition.; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[3 1,3 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[3 4,3 4], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.a), and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 3))), and(json_memberof(cast(3, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 4))) | ||
| └─TableRowIDScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where (1 member of (a) and 1 member of (b) and c=3) or (3 member of (b) and c=4) or e=1; -- 8: OR index merge from multi complicated mv index (memberof), each DNF item should be strict or lax used as index partial path.; | ||
| id task access object operator info | ||
| TableReader root data:Selection | ||
| └─Selection cop[tikv] or(and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.a), and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 3))), or(and(json_memberof(cast(3, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 4)), eq(planner__core__casetest__index__index.t2.e, 1))) | ||
| └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ use_index_merge(t2, idx2, idx, idx4) */ * from t2 where (1 member of (a) and 1 member of (b) and c=3) or (3 member of (b) and c=4) or d=1; -- 9: OR index merge from multi complicated mv index (memberof), each DNF item should be strict or lax used as index partial path, specify the index in index merge hint; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[3 1,3 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[3 4,3 4], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx4(d) range:[1,1], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.a), and(json_memberof(cast(1, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 3))), or(and(json_memberof(cast(3, json BINARY), planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t2.c, 4)), eq(planner__core__casetest__index__index.t2.d, 1))) | ||
| └─TableRowIDScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| drop table if exists t1, t2; | ||
| create table t1(pk int primary key, a json, b json, c int, d int, index idx((cast(a as signed array))), index idx2((cast(b as signed array)))); | ||
| create table t2(pk int primary key, a json, b json, c int, d int, index idx(c, (cast(a as signed array))), index idx2((cast(b as signed array)), c), index idx3(c, d), index idx4(d)); | ||
| explain format = 'brief' select /*+ use_index_merge(t1, idx2, idx) */ * from t1 where 1 member of (a) and 2 member of (b); -- 1: AND index merge from multi member mv index predicate, since member of is single partial path, it can be merged with outer index merge.; | ||
| id estRows task access object operator info | ||
| IndexMerge 0.01 root type: intersection | ||
| ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t1, index:idx(cast(`a` as signed array)) range:[1,1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t1, index:idx2(cast(`b` as signed array)) range:[2,2], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) 0.01 cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| explain format = 'brief' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where 1 member of (a) and c=1 and 2 member of (b); -- 2: AND index merge from multi complicated mv index; | ||
| id estRows task access object operator info | ||
| IndexMerge 0.00 root type: intersection | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format = 'brief' select /*+ use_index_merge(t2, idx2, idx, idx4) */ * from t2 where 1 member of (a) and c=1 and 2 member of (b) and d=3; -- 3: AND index merge from multi complicated mv indexes and normal indexes; | ||
| id estRows task access object operator info | ||
| IndexMerge 0.00 root type: intersection | ||
| ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t2, index:idx4(d) range:[3,3], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format = 'brief' select /*+ use_index_merge(t2, idx2, idx, idx3) */ * from t2 where json_contains(a, '[1, 2, 3]') and c=1 and 2 member of (b) and d=3; -- 4: AND index merge from multi complicated mv indexes (json_contains (intersection))and normal indexes; | ||
| id estRows task access object operator info | ||
| IndexMerge 0.00 root type: intersection | ||
| ├─IndexRangeScan(Build) 1.25 cop[tikv] table:t2, index:idx3(c, d) range:[1 3,1 3], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 1,1 1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 2,1 2], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx(c, cast(`a` as signed array)) range:[1 3,1 3], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format = 'brief' select /*+ use_index_merge(t2, idx2, idx, idx3) */ * from t2 where json_overlaps(a, '[1, 2, 3]') and c=1 and 2 member of (b) and d=3; -- 5: AND index merge from multi complicated mv indexes (json_overlap (intersection))and normal indexes; | ||
| id estRows task access object operator info | ||
| Selection 0.24 root json_overlaps(planner__core__casetest__index__index.t2.a, cast("[1, 2, 3]", json BINARY)) | ||
| └─IndexMerge 0.00 root type: intersection | ||
| ├─IndexRangeScan(Build) 1.25 cop[tikv] table:t2, index:idx3(c, d) range:[1 3,1 3], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) 0.10 cop[tikv] table:t2, index:idx2(cast(`b` as signed array), c) range:[2 1,2 1], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| explain format = 'brief' select /*+ use_index_merge(t2, idx2, idx) */ * from t2 where 1 member of (a) and c=1 and c=2; -- 6: AND index merge from multi complicated mv indexes (empty range); | ||
| id estRows task access object operator info | ||
| TableDual 0.00 root rows:0 | ||
| drop table if exists t; | ||
| create table t(a int, b int, c int, unique index(a), unique index(b), primary key(c)); | ||
| explain format='plan_tree' select /*+ USE_INDEX_MERGE(t, a, b) */ * from t where a = 1 or b = 2; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[2,2], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ USE_INDEX_MERGE(t, A, B) */ * from t where a = 1 or b = 2; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[2,2], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ USE_INDEX_MERGE(t, primary) */ * from t where 1 or t.c; | ||
| id task access object operator info | ||
| TableReader root data:TableFullScan | ||
| └─TableFullScan cop[tikv] table:t keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ USE_INDEX_MERGE(t, a, b, c) */ * from t where 1 or t.a = 1 or t.b = 2; | ||
| id task access object operator info | ||
| TableReader root data:TableFullScan | ||
| └─TableFullScan cop[tikv] table:t keep order:false, stats:pseudo | ||
| drop table if exists t1, t2, t3, t4; | ||
| create table t1(a int, b int, primary key (a), key idx_t1_b (b)); | ||
| create table t2(a int, b int, primary key (a), key idx_t1_b (b)); | ||
| create table t3(a int, b int, c int); | ||
| create table t4(a int, b int, c int, primary key (a, b) clustered); | ||
| explain format='plan_tree' select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.b; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t1.a, inner key:planner__core__casetest__index__index.t2.a, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a), eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.b) | ||
| ├─IndexReader(Build) root index:IndexFullScan | ||
| │ └─IndexFullScan cop[tikv] table:t1, index:idx_t1_b(b) keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.b)) | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [planner__core__casetest__index__index.t1.a], keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.a and t1.b = t2.b; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t1.b, inner key:planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t2.a, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a), eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.a), eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.b) | ||
| ├─IndexReader(Build) root index:IndexFullScan | ||
| │ └─IndexFullScan cop[tikv] table:t1, index:idx_t1_b(b) keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.b)) | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [planner__core__casetest__index__index.t1.a planner__core__casetest__index__index.t1.b], keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.a = t4.a where t4.b = 1; | ||
| id task access object operator info | ||
| Projection root planner__core__casetest__index__index.t3.a, planner__core__casetest__index__index.t3.b, planner__core__casetest__index__index.t3.c, planner__core__casetest__index__index.t4.a, planner__core__casetest__index__index.t4.b, planner__core__casetest__index__index.t4.c | ||
| └─IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t3.a, inner key:planner__core__casetest__index__index.t4.a, equal cond:eq(planner__core__casetest__index__index.t3.a, planner__core__casetest__index__index.t4.a) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t3.a)) | ||
| │ └─TableFullScan cop[tikv] table:t3 keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] eq(planner__core__casetest__index__index.t4.b, 1) | ||
| └─TableRangeScan cop[tikv] table:t4 range: decided by [eq(planner__core__casetest__index__index.t4.a, planner__core__casetest__index__index.t3.a) eq(planner__core__casetest__index__index.t4.b, 1)], keep order:false, stats:pseudo | ||
| explain format='plan_tree' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.b = t4.b where t4.a = 1; | ||
| id task access object operator info | ||
| Projection root planner__core__casetest__index__index.t3.a, planner__core__casetest__index__index.t3.b, planner__core__casetest__index__index.t3.c, planner__core__casetest__index__index.t4.a, planner__core__casetest__index__index.t4.b, planner__core__casetest__index__index.t4.c | ||
| └─IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t3.b, inner key:planner__core__casetest__index__index.t4.b, equal cond:eq(planner__core__casetest__index__index.t3.b, planner__core__casetest__index__index.t4.b) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t3.b)) | ||
| │ └─TableFullScan cop[tikv] table:t3 keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] eq(planner__core__casetest__index__index.t4.a, 1) | ||
| └─TableRangeScan cop[tikv] table:t4 range: decided by [eq(planner__core__casetest__index__index.t4.b, planner__core__casetest__index__index.t3.b) eq(planner__core__casetest__index__index.t4.a, 1)], keep order:false, stats:pseudo | ||
| drop table if exists t1, t2; | ||
| create table t1(a int not null, b int not null, c int not null); | ||
| create table t2(a int not null, b int not null, c int not null, index idx1(a,b), index idx2(c)); | ||
| insert into t1 values(1,1,1),(1,1,1),(1,1,1); | ||
| insert into t2 values(1,1,1),(1,1,2),(1,1,3); | ||
| analyze table t1, t2 all columns; | ||
| explain format='plan_tree' select /*+ inl_join(t2) */ * from t1, t2 where t1.a = t2.a and t1.b = t2.b and t1.c = t2.c; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t1.b, inner key:planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t2.b, equal cond:eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.a), eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.b), eq(planner__core__casetest__index__index.t1.c, planner__core__casetest__index__index.t2.c) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─IndexLookUp(Probe) root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t2, index:idx1(a, b) range: decided by [eq(planner__core__casetest__index__index.t2.a, planner__core__casetest__index__index.t1.a) eq(planner__core__casetest__index__index.t2.b, planner__core__casetest__index__index.t1.b)], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false | ||
| drop table if exists t; | ||
| create table t (a int, b int, unique key(a), unique key(b)); | ||
| insert into t value (1, 5), (2, 4), (3, 3), (4, 2), (5, 1); | ||
| insert into t value (6, 0), (7, -1), (8, -2), (9, -3), (10, -4); | ||
| analyze table t; | ||
| explain format='plan_tree' select /*+ use_index_merge(t) */ * from t where a =1 or (b=1 and b+2>1); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[1,1], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format='plan_tree' select /*+ use_index_merge(t) */ * from t where a =1 or (b=1 and length(b)=1); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[1,1], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format='plan_tree' select /*+ use_index_merge(t) */ * from t where (a=1 and length(a)=1) or (b=1 and length(b)=1); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[1,1], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format='plan_tree' select /*+ use_index_merge(t) */ * from t where (a=1 and length(b)=1) or (b=1 and length(a)=1); | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:a(a) range:[1,1], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:b(b) range:[1,1], keep order:false | ||
| └─Selection(Probe) cop[tikv] or(and(eq(planner__core__casetest__index__index.t.a, 1), eq(length(cast(planner__core__casetest__index__index.t.b, var_string(20))), 1)), and(eq(planner__core__casetest__index__index.t.b, 1), eq(length(cast(planner__core__casetest__index__index.t.a, var_string(20))), 1))) | ||
| └─TableRowIDScan cop[tikv] table:t keep order:false | ||
| show warnings; | ||
| Level Code Message | ||
| set tidb_enable_clustered_index=on; | ||
| drop table if exists t; | ||
| create table t (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c)); | ||
| insert into t values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13); | ||
| analyze table t; | ||
| explain format='plan_tree' select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t.a, inner key:planner__core__casetest__index__index.t.a, equal cond:eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─TableReader(Probe) root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a)], keep order:false | ||
| select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| a b c d a b c d | ||
| 1 111 1.1000000000 11 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ inl_merge_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| id task access object operator info | ||
| MergeJoin root inner join, left key:planner__core__casetest__index__index.t.a, right key:planner__core__casetest__index__index.t.a | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:true | ||
| └─TableReader(Probe) root data:TableFullScan | ||
| └─TableFullScan cop[tikv] table:t1 keep order:true | ||
| select /*+ inl_merge_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| a b c d a b c d | ||
| 1 111 1.1000000000 11 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ inl_hash_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| id task access object operator info | ||
| IndexHashJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t.a, inner key:planner__core__casetest__index__index.t.a, equal cond:eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─TableReader(Probe) root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a)], keep order:false | ||
| select /*+ inl_hash_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a; | ||
| a b c d a b c d | ||
| 1 111 1.1000000000 11 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a and t1.b = t2.b; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:TableReader, outer key:planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.b, inner key:planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.b, equal cond:eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a), eq(planner__core__casetest__index__index.t.b, planner__core__casetest__index__index.t.b) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─TableReader(Probe) root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [eq(planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.a) eq(planner__core__casetest__index__index.t.b, planner__core__casetest__index__index.t.b)], keep order:false | ||
| select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.a = t2.a and t1.b = t2.b; | ||
| a b c d a b c d | ||
| 1 111 1.1000000000 11 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.c = t2.c; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t.c, inner key:planner__core__casetest__index__index.t.c, equal cond:eq(planner__core__casetest__index__index.t.c, planner__core__casetest__index__index.t.c) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t.c)) | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t.c)) | ||
| │ └─IndexRangeScan cop[tikv] table:t2, index:c(c) range: decided by [eq(planner__core__casetest__index__index.t.c, planner__core__casetest__index__index.t.c)], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t2 keep order:false | ||
| select /*+ inl_join(t1, t2) */ * from t t1 join t t2 on t1.c = t2.c; | ||
| a b c d a b c d | ||
| 1 111 1.1000000000 11 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ inl_merge_join(t1,t2) */ t2.a, t2.c, t2.d from t t1 left join t t2 on t1.a = t2.c; | ||
| id task access object operator info | ||
| HashJoin root left outer join, left side:Projection, equal:[eq(Column, planner__core__casetest__index__index.t.c)] | ||
| ├─Projection(Build) root cast(planner__core__casetest__index__index.t.a, decimal(10,0) BINARY)->Column | ||
| │ └─IndexReader root index:IndexFullScan | ||
| │ └─IndexFullScan cop[tikv] table:t1, index:c(c) keep order:false | ||
| └─TableReader(Probe) root data:TableFullScan | ||
| └─TableFullScan cop[tikv] table:t2 keep order:false | ||
| select /*+ inl_merge_join(t1,t2) */ t2.a, t2.c, t2.d from t t1 left join t t2 on t1.a = t2.c; | ||
| a c d | ||
| NULL NULL NULL | ||
| NULL NULL NULL | ||
| NULL NULL NULL | ||
| set tidb_enable_clustered_index=default; | ||
| drop table if exists t1, t2; | ||
| create table t1(c1 int, c2 int, c3 int, primary key(c1), key(c2)); | ||
| insert into t1 values(1, 1, 1); | ||
| insert into t1 values(2, 2, 2); | ||
| create table t2(c1 int, c2 int, c3 int); | ||
| insert into t2 values(1, 1, 1); | ||
| insert into t2 values(2, 2, 2); | ||
| drop table if exists tt1, tt2; | ||
| create table tt1 (c_int int, c_str varchar(40), c_datetime datetime, c_decimal decimal(12, 6), primary key(c_int), key(c_int), key(c_str), unique key(c_decimal), key(c_datetime)); | ||
| create table tt2 like tt1 ; | ||
| insert into tt1 (c_int, c_str, c_datetime, c_decimal) values (6, 'sharp payne', '2020-06-07 10:40:39', 6.117000) , | ||
| (7, 'objective kare', '2020-02-05 18:47:26', 1.053000) , | ||
| (8, 'thirsty pasteur', '2020-01-02 13:06:56', 2.506000) , | ||
| (9, 'blissful wilbur', '2020-06-04 11:34:04', 9.144000) , | ||
| (10, 'reverent mclean', '2020-02-12 07:36:26', 7.751000) ; | ||
| insert into tt2 (c_int, c_str, c_datetime, c_decimal) values (6, 'beautiful joliot', '2020-01-16 01:44:37', 5.627000) , | ||
| (7, 'hopeful blackburn', '2020-05-23 21:44:20', 7.890000) , | ||
| (8, 'ecstatic davinci', '2020-02-01 12:27:17', 5.648000) , | ||
| (9, 'hopeful lewin', '2020-05-05 05:58:25', 7.288000) , | ||
| (10, 'sharp jennings', '2020-01-28 04:35:03', 9.758000) ; | ||
| explain format='plan_tree' select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 = 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and substring(c3, 10)) order by c1; | ||
| id task access object operator info | ||
| Sort root planner__core__casetest__index__index.t2.c1 | ||
| └─Projection root planner__core__casetest__index__index.t2.c1, planner__core__casetest__index__index.t2.c2, planner__core__casetest__index__index.t2.c3 | ||
| └─Apply root CARTESIAN inner join, other cond:or(and(lt(planner__core__casetest__index__index.t2.c1, Column), if(ne(Column, 0), NULL, 1)), or(eq(Column, 0), if(isnull(planner__core__casetest__index__index.t2.c1), NULL, 0))) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─StreamAgg(Probe) root funcs:min(planner__core__casetest__index__index.t1.c1)->Column, funcs:sum(0)->Column, funcs:count(1)->Column | ||
| └─IndexMerge root type: union | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t2.c3, 10) | ||
| │ └─TableRangeScan cop[tikv] table:t1 range:[10,10], keep order:false, stats:pseudo | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t2.c3, 1) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:c2(c2) range:[1,1], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(eq(planner__core__casetest__index__index.t1.c1, 10), eq(planner__core__casetest__index__index.t2.c3, 10)), and(eq(planner__core__casetest__index__index.t1.c2, 1), eq(planner__core__casetest__index__index.t2.c3, 1))), substring(cast(planner__core__casetest__index__index.t1.c3, var_string(20)), 10) | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 = 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and substring(c3, 10)) order by c1; | ||
| c1 c2 c3 | ||
| 1 1 1 | ||
| 2 2 2 | ||
| explain format='plan_tree' select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 = 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and reverse(c3)) order by c1; | ||
| id task access object operator info | ||
| Sort root planner__core__casetest__index__index.t2.c1 | ||
| └─Projection root planner__core__casetest__index__index.t2.c1, planner__core__casetest__index__index.t2.c2, planner__core__casetest__index__index.t2.c3 | ||
| └─Apply root CARTESIAN inner join, other cond:or(and(lt(planner__core__casetest__index__index.t2.c1, Column), if(ne(Column, 0), NULL, 1)), or(eq(Column, 0), if(isnull(planner__core__casetest__index__index.t2.c1), NULL, 0))) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─StreamAgg(Probe) root funcs:min(planner__core__casetest__index__index.t1.c1)->Column, funcs:sum(0)->Column, funcs:count(1)->Column | ||
| └─IndexMerge root type: union | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t2.c3, 10) | ||
| │ └─TableRangeScan cop[tikv] table:t1 range:[10,10], keep order:false, stats:pseudo | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t2.c3, 1) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:c2(c2) range:[1,1], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(eq(planner__core__casetest__index__index.t1.c1, 10), eq(planner__core__casetest__index__index.t2.c3, 10)), and(eq(planner__core__casetest__index__index.t1.c2, 1), eq(planner__core__casetest__index__index.t2.c3, 1))), reverse(cast(planner__core__casetest__index__index.t1.c3, var_string(20))) | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 = 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and reverse(c3)) order by c1; | ||
| c1 c2 c3 | ||
| 2 2 2 | ||
| explain format='plan_tree' select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 >= 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and substring(c3, 10)) order by c1; | ||
| id task access object operator info | ||
| Sort root planner__core__casetest__index__index.t2.c1 | ||
| └─Projection root planner__core__casetest__index__index.t2.c1, planner__core__casetest__index__index.t2.c2, planner__core__casetest__index__index.t2.c3 | ||
| └─Apply root CARTESIAN inner join, other cond:or(and(lt(planner__core__casetest__index__index.t2.c1, Column), if(ne(Column, 0), NULL, 1)), or(eq(Column, 0), if(isnull(planner__core__casetest__index__index.t2.c1), NULL, 0))) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─StreamAgg(Probe) root funcs:min(planner__core__casetest__index__index.t1.c1)->Column, funcs:sum(0)->Column, funcs:count(1)->Column | ||
| └─IndexMerge root type: union | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t1.c1, planner__core__casetest__index__index.t2.c3) | ||
| │ └─TableRangeScan cop[tikv] table:t1 range:[10,+inf], keep order:false, stats:pseudo | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.t2.c3, 1) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:c2(c2) range:[1,1], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(ge(planner__core__casetest__index__index.t1.c1, 10), eq(planner__core__casetest__index__index.t1.c1, planner__core__casetest__index__index.t2.c3)), and(eq(planner__core__casetest__index__index.t1.c2, 1), eq(planner__core__casetest__index__index.t2.c3, 1))), substring(cast(planner__core__casetest__index__index.t1.c3, var_string(20)), 10) | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| select * from t2 where c1 < all(select /*+ use_index_merge(t1) */ c1 from t1 where (c1 >= 10 and c1 = t2.c3 or c2 = 1 and c2 = t2.c3) and substring(c3, 10)) order by c1; | ||
| c1 c2 c3 | ||
| 1 1 1 | ||
| 2 2 2 | ||
| explain format='plan_tree' select c_int from tt1 where c_decimal < all (select /*+ use_index_merge(tt2) */ c_decimal from tt2 where tt1.c_int = tt2.c_int and tt1.c_datetime > tt2.c_datetime and tt2.c_decimal = 9.060 or tt2.c_str <= 'interesting shtern' and tt1.c_int = tt2.c_int) order by 1; | ||
| id task access object operator info | ||
| Projection root planner__core__casetest__index__index.tt1.c_int | ||
| └─Apply root CARTESIAN inner join, other cond:or(and(lt(planner__core__casetest__index__index.tt1.c_decimal, Column), if(ne(Column, 0), NULL, 1)), or(eq(Column, 0), if(isnull(planner__core__casetest__index__index.tt1.c_decimal), NULL, 0))) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:tt1 keep order:true, stats:pseudo | ||
| └─StreamAgg(Probe) root funcs:min(Column)->Column, funcs:sum(Column)->Column, funcs:count(1)->Column | ||
| └─Projection root planner__core__casetest__index__index.tt2.c_decimal->Column, cast(isnull(planner__core__casetest__index__index.tt2.c_decimal), decimal(20,0) BINARY)->Column | ||
| └─IndexMerge root type: union | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int) | ||
| │ └─IndexRangeScan cop[tikv] table:tt2, index:c_decimal(c_decimal) range:[9.060000,9.060000], keep order:false, stats:pseudo | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int) | ||
| │ └─IndexRangeScan cop[tikv] table:tt2, index:c_str(c_str) range:[-inf,"interesting shtern"], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int), and(gt(planner__core__casetest__index__index.tt1.c_datetime, planner__core__casetest__index__index.tt2.c_datetime), eq(planner__core__casetest__index__index.tt2.c_decimal, 9.060))), and(le(planner__core__casetest__index__index.tt2.c_str, "interesting shtern"), eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int))) | ||
| └─TableRowIDScan cop[tikv] table:tt2 keep order:false, stats:pseudo | ||
| select c_int from tt1 where c_decimal < all (select /*+ use_index_merge(tt2) */ c_decimal from tt2 where tt1.c_int = tt2.c_int and tt1.c_datetime > tt2.c_datetime and tt2.c_decimal = 9.060 or tt2.c_str <= 'interesting shtern' and tt1.c_int = tt2.c_int) order by 1; | ||
| c_int | ||
| 7 | ||
| 8 | ||
| 10 | ||
| explain format='plan_tree' select c_int from tt1 where c_decimal > all (select /*+ use_index_merge(tt2) */ c_decimal from tt2 where tt2.c_int = 7 and tt2.c_int < tt1.c_decimal or tt2.c_str >= 'zzzzzzzzzzzzzzzzzzz' and tt1.c_int = tt2.c_int) order by 1; | ||
| id task access object operator info | ||
| Projection root planner__core__casetest__index__index.tt1.c_int | ||
| └─Apply root CARTESIAN inner join, other cond:or(and(gt(planner__core__casetest__index__index.tt1.c_decimal, Column), if(ne(Column, 0), NULL, 1)), or(eq(Column, 0), if(isnull(planner__core__casetest__index__index.tt1.c_decimal), NULL, 0))) | ||
| ├─TableReader(Build) root data:TableFullScan | ||
| │ └─TableFullScan cop[tikv] table:tt1 keep order:true, stats:pseudo | ||
| └─StreamAgg(Probe) root funcs:max(Column)->Column, funcs:sum(Column)->Column, funcs:count(1)->Column | ||
| └─Projection root planner__core__casetest__index__index.tt2.c_decimal->Column, cast(isnull(planner__core__casetest__index__index.tt2.c_decimal), decimal(20,0) BINARY)->Column | ||
| └─IndexMerge root type: union | ||
| ├─Selection(Build) cop[tikv] lt(7, planner__core__casetest__index__index.tt1.c_decimal) | ||
| │ └─TableRangeScan cop[tikv] table:tt2 range:[7,7], keep order:false, stats:pseudo | ||
| ├─Selection(Build) cop[tikv] eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int) | ||
| │ └─IndexRangeScan cop[tikv] table:tt2, index:c_str(c_str) range:["zzzzzzzzzzzzzzzzzzz",+inf], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] or(and(eq(planner__core__casetest__index__index.tt2.c_int, 7), lt(7, planner__core__casetest__index__index.tt1.c_decimal)), and(ge(planner__core__casetest__index__index.tt2.c_str, "zzzzzzzzzzzzzzzzzzz"), eq(planner__core__casetest__index__index.tt1.c_int, planner__core__casetest__index__index.tt2.c_int))) | ||
| └─TableRowIDScan cop[tikv] table:tt2 keep order:false, stats:pseudo | ||
| select c_int from tt1 where c_decimal > all (select /*+ use_index_merge(tt2) */ c_decimal from tt2 where tt2.c_int = 7 and tt2.c_int < tt1.c_decimal or tt2.c_str >= 'zzzzzzzzzzzzzzzzzzz' and tt1.c_int = tt2.c_int) order by 1; | ||
| c_int | ||
| 6 | ||
| 7 | ||
| 8 | ||
| 9 | ||
| drop table if exists t1, t2; | ||
| create table t1(a int, b int, c varchar(10), d varchar(10), index idx_a_b_c_d(a, b, c(2), d(2))); | ||
| create table t2(e int, f int, g varchar(10), h varchar(10)); | ||
| set @@tidb_opt_range_max_size = 0; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.b = t2.e and t1.d = t2.g where t1.a in (1, 3) and t1.c in ('aaa', 'bbb'); | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t2.g, inner key:planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t1.d, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.b), eq(planner__core__casetest__index__index.t2.g, planner__core__casetest__index__index.t1.d) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.g)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.b)), not(isnull(planner__core__casetest__index__index.t1.d)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.e) eq(planner__core__casetest__index__index.t1.d, planner__core__casetest__index__index.t2.g) in(planner__core__casetest__index__index.t1.a, 1, 3) in(planner__core__casetest__index__index.t1.c, aaa, bbb)], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] in(planner__core__casetest__index__index.t1.c, "aaa", "bbb") | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| set @@tidb_opt_range_max_size = 2900; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.b = t2.e and t1.d = t2.g where t1.a in (1, 3) and t1.c in ('aaa', 'bbb'); | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.b, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.b), eq(planner__core__casetest__index__index.t2.g, planner__core__casetest__index__index.t1.d) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.g)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.b)), not(isnull(planner__core__casetest__index__index.t1.d)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.e) in(planner__core__casetest__index__index.t1.a, 1, 3) in(planner__core__casetest__index__index.t1.c, aaa, bbb)], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] in(planner__core__casetest__index__index.t1.c, "aaa", "bbb") | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| Warning 1105 Memory capacity of 2900 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen | ||
| set @@tidb_opt_range_max_size = 2300; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.b = t2.e and t1.d = t2.g where t1.a in (1, 3) and t1.c in ('aaa', 'bbb'); | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.b, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.b), eq(planner__core__casetest__index__index.t2.g, planner__core__casetest__index__index.t1.d) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.g)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.b)), not(isnull(planner__core__casetest__index__index.t1.d)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.e) in(planner__core__casetest__index__index.t1.a, 1, 3)], keep order:false, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] in(planner__core__casetest__index__index.t1.c, "aaa", "bbb") | ||
| └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| Warning 1105 Memory capacity of 2300 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen | ||
| set @@tidb_opt_range_max_size = 700; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.b = t2.e and t1.d = t2.g where t1.a in (1, 3) and t1.c in ('aaa', 'bbb'); | ||
| id task access object operator info | ||
| HashJoin root inner join, equal:[eq(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.e) eq(planner__core__casetest__index__index.t1.d, planner__core__casetest__index__index.t2.g)] | ||
| ├─IndexLookUp(Build) root | ||
| │ ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.b)), not(isnull(planner__core__casetest__index__index.t1.d)) | ||
| │ │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range:[1,1], [3,3], keep order:false, stats:pseudo | ||
| │ └─Selection(Probe) cop[tikv] in(planner__core__casetest__index__index.t1.c, "aaa", "bbb") | ||
| │ └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.g)) | ||
| └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| Warning 1105 Memory capacity of 700 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen | ||
| Warning 1815 Optimizer Hint /*+ INL_JOIN(t1) */ or /*+ TIDB_INLJ(t1) */ is inapplicable | ||
| set @@tidb_opt_range_max_size = 0; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.e where t1.b > 1 and t1.b < 10; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.a, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.a) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.a)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.e) gt(planner__core__casetest__index__index.t1.b, 1) lt(planner__core__casetest__index__index.t1.b, 10)], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| set @@tidb_opt_range_max_size = 300; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.e where t1.b > 1 and t1.b < 10; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.a, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.a) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] gt(planner__core__casetest__index__index.t1.b, 1), lt(planner__core__casetest__index__index.t1.b, 10), not(isnull(planner__core__casetest__index__index.t1.a)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.e)], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| Warning 1105 Memory capacity of 300 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen | ||
| set @@tidb_opt_range_max_size = 0; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.e where t1.b > t2.f and t1.b < t2.f + 10; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.a, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.a), other cond:gt(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.f), lt(planner__core__casetest__index__index.t1.b, plus(planner__core__casetest__index__index.t2.f, 10)) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.f)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.a)), not(isnull(planner__core__casetest__index__index.t1.b)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.e) gt(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.f) lt(planner__core__casetest__index__index.t1.b, plus(planner__core__casetest__index__index.t2.f, 10))], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| set @@tidb_opt_range_max_size = 300; | ||
| explain format='plan_tree' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.e where t1.b > t2.f and t1.b < t2.f + 10; | ||
| id task access object operator info | ||
| IndexJoin root inner join, inner:IndexLookUp, outer key:planner__core__casetest__index__index.t2.e, inner key:planner__core__casetest__index__index.t1.a, equal cond:eq(planner__core__casetest__index__index.t2.e, planner__core__casetest__index__index.t1.a), other cond:gt(planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t2.f), lt(planner__core__casetest__index__index.t1.b, plus(planner__core__casetest__index__index.t2.f, 10)) | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t2.e)), not(isnull(planner__core__casetest__index__index.t2.f)) | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─IndexLookUp(Probe) root | ||
| ├─Selection(Build) cop[tikv] not(isnull(planner__core__casetest__index__index.t1.a)), not(isnull(planner__core__casetest__index__index.t1.b)) | ||
| │ └─IndexRangeScan cop[tikv] table:t1, index:idx_a_b_c_d(a, b, c, d) range: decided by [eq(planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t2.e)], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| Warning 1105 Memory capacity of 300 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen | ||
| set @@tidb_opt_range_max_size = default; | ||
| drop table if exists t1, t2, t3, t4; | ||
| create table t1(a int, b int, c int, d int, e int, f int, g int, primary key (a), unique key c_d_e (c, d, e), unique key f (f), unique key f_g (f, g), key g (g)); | ||
| create table t2(a int, b int, c int, d int, unique index idx_a (a), unique index idx_b_c (b, c), unique index idx_b_c_a_d (b, c, a, d)); | ||
| create table t3(a bigint, b varchar(255), c bigint, primary key(a, b) clustered); | ||
| create table t4(a bigint, b varchar(255), c bigint, primary key(a, b) nonclustered); | ||
| set @@tidb_enable_chunk_rpc = on; | ||
| explain format = 'brief' select * from t1 where a = 3 or a = 5; | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 2.00 root table:t1 handle:[3 5], keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select f, g from t1 where f = 2 and g in (3, 4, 5); | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 3.00 root table:t1, index:f_g(f, g) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select * from t1 where c = 1 and (d = 2 or d = 3) and e in (4, 5); | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 4.00 root table:t1, index:c_d_e(c, d, e) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select f, g from t1 where f = 2 and g > 3; | ||
| id estRows task access object operator info | ||
| IndexReader 33.33 root index:IndexRangeScan | ||
| └─IndexRangeScan 33.33 cop[tikv] table:t1, index:f_g(f, g) range:(2 3,2 +inf], keep order:false, stats:pseudo | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select a, b, c from t2 where a = 1 and b = 2 and c in (1, 2, 3, 4, 5); | ||
| id estRows task access object operator info | ||
| Selection 1.00 root eq(planner__core__casetest__index__index.t2.b, 2), in(planner__core__casetest__index__index.t2.c, 1, 2, 3, 4, 5) | ||
| └─Point_Get 1.00 root table:t2, index:idx_a(a) | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select * from t3 where (a = 1 or a = 3) and b = 'xx'; | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 2.00 root table:t3, clustered index:PRIMARY(a, b) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select * from t4 where (a = 1 or a = 3) and b = 'xx'; | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 2.00 root table:t4, index:PRIMARY(a, b) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select a, b from t3 where (a = 1 or a = 3) and b = 'xx'; | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 2.00 root table:t3, clustered index:PRIMARY(a, b) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' select a, b from t4 where (a = 1 or a = 3) and b = 'xx'; | ||
| id estRows task access object operator info | ||
| Batch_Point_Get 2.00 root table:t4, index:PRIMARY(a, b) keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' update t1 set b = 2 where a = 4 or a = 6; | ||
| id estRows task access object operator info | ||
| Update N/A root N/A | ||
| └─Batch_Point_Get 2.00 root table:t1 handle:[4 6], keep order:false, desc:false | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' delete from t1 where f = 2 and g in (3, 4); | ||
| id estRows task access object operator info | ||
| Delete N/A root N/A | ||
| └─Selection 2.00 root in(planner__core__casetest__index__index.t1.g, 3, 4) | ||
| └─Point_Get 1.00 root table:t1, index:f(f) | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' insert into t3 select a, b, c from t1 where f = 2; | ||
| id estRows task access object operator info | ||
| Insert N/A root N/A | ||
| └─Projection 1.00 root planner__core__casetest__index__index.t1.a, planner__core__casetest__index__index.t1.b, planner__core__casetest__index__index.t1.c | ||
| └─Point_Get 1.00 root table:t1, index:f(f) | ||
| show warnings; | ||
| Level Code Message | ||
| explain format = 'brief' replace into t3 select a, b, c from t1 where a = 3; | ||
| id estRows task access object operator info | ||
| Insert N/A root N/A | ||
| └─Point_Get 1.00 root table:t1 handle:3 | ||
| show warnings; | ||
| Level Code Message | ||
| set @@tidb_enable_chunk_rpc = default; | ||
| drop table if exists t; | ||
| create table t(a int, b int, c int, d int, index idx(a,b,c)); | ||
| explain format='plan_tree' select * from t where a = 1 and b > 2 and b < 10 and d = 10 order by b,c limit 10; | ||
| id task access object operator info | ||
| Limit root offset:0, count:10 | ||
| └─Projection root planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.b, planner__core__casetest__index__index.t.c, planner__core__casetest__index__index.t.d | ||
| └─IndexLookUp root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, b, c) range:(1 2,1 10), keep order:true, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] eq(planner__core__casetest__index__index.t.d, 10) | ||
| └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo | ||
| explain format='plan_tree' select * from t where a = 1 and b > 2 and b < 10 and d = 10 order by b desc, c desc limit 10; | ||
| id task access object operator info | ||
| Limit root offset:0, count:10 | ||
| └─Projection root planner__core__casetest__index__index.t.a, planner__core__casetest__index__index.t.b, planner__core__casetest__index__index.t.c, planner__core__casetest__index__index.t.d | ||
| └─IndexLookUp root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, b, c) range:(1 2,1 10), keep order:true, desc, stats:pseudo | ||
| └─Selection(Probe) cop[tikv] eq(planner__core__casetest__index__index.t.d, 10) | ||
| └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo | ||
| set tidb_enable_clustered_index=on; | ||
| drop table if exists t1; | ||
| create table t1 (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c)); | ||
| insert into t1 values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13); | ||
| analyze table t1; | ||
| explain format='plan_tree' select * from t1; | ||
| id task access object operator info | ||
| TableReader root data:TableFullScan | ||
| └─TableFullScan cop[tikv] table:t1 keep order:false | ||
| select * from t1; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select * from t1 where t1.a >= 1 and t1.a < 4; | ||
| id task access object operator info | ||
| TableReader root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t1 range:[1,4), keep order:false | ||
| select * from t1 where t1.a >= 1 and t1.a < 4; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select * from t1 where t1.a = 1 and t1.b < "333"; | ||
| id task access object operator info | ||
| TableReader root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t1 range:[1 -inf,1 "333"), keep order:false | ||
| select * from t1 where t1.a = 1 and t1.b < "333"; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| explain format='plan_tree' select t1.a, t1.b, t1.c from t1 where t1.c = 3.3; | ||
| id task access object operator info | ||
| IndexReader root index:IndexRangeScan | ||
| └─IndexRangeScan cop[tikv] table:t1, index:c(c) range:[3.3000000000,3.3000000000], keep order:false | ||
| select t1.a, t1.b, t1.c from t1 where t1.c = 3.3; | ||
| a b c | ||
| 3 333 3.3000000000 | ||
| explain format='plan_tree' select t1.b, t1.c from t1 where t1.c = 2.2; | ||
| id task access object operator info | ||
| IndexReader root index:IndexRangeScan | ||
| └─IndexRangeScan cop[tikv] table:t1, index:c(c) range:[2.2000000000,2.2000000000], keep order:false | ||
| select t1.b, t1.c from t1 where t1.c = 2.2; | ||
| b c | ||
| 222 2.2000000000 | ||
| explain format='plan_tree' select /*+ use_index(t1, c) */ * from t1; | ||
| id task access object operator info | ||
| IndexLookUp root | ||
| ├─IndexFullScan(Build) cop[tikv] table:t1, index:c(c) keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false | ||
| select /*+ use_index(t1, c) */ * from t1; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select * from t1 use index(c) where t1.c in (2.2, 3.3); | ||
| id task access object operator info | ||
| IndexLookUp root | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t1, index:c(c) range:[2.2000000000,2.2000000000], [3.3000000000,3.3000000000], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false | ||
| select * from t1 use index(c) where t1.c in (2.2, 3.3); | ||
| a b c d | ||
| 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select * from t1 where t1.a = 1 order by b; | ||
| id task access object operator info | ||
| TableReader root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t1 range:[1,1], keep order:true | ||
| select * from t1 where t1.a = 1 order by b; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| explain format='plan_tree' select * from t1 order by a, b limit 1; | ||
| id task access object operator info | ||
| Limit root offset:0, count:1 | ||
| └─TableReader root data:Limit | ||
| └─Limit cop[tikv] offset:0, count:1 | ||
| └─TableFullScan cop[tikv] table:t1 keep order:true | ||
| select * from t1 order by a, b limit 1; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| explain format='plan_tree' select /*+ use_index_merge(t1 primary, c) */ * from t1 where t1.a >= 1 or t1.c = 2.2; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─TableRangeScan(Build) cop[tikv] table:t1 range:[1,+inf], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t1, index:c(c) range:[2.2000000000,2.2000000000], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false | ||
| select /*+ use_index_merge(t1 primary, c) */ * from t1 where t1.a >= 1 or t1.c = 2.2; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| 2 222 2.2000000000 12 | ||
| 3 333 3.3000000000 13 | ||
| explain format='plan_tree' select /*+ use_index_merge(t1 primary, c) */ * from t1 where t1.a = 1 and t1.b = '111' or t1.c = 3.3; | ||
| id task access object operator info | ||
| IndexMerge root type: union | ||
| ├─TableRangeScan(Build) cop[tikv] table:t1 range:[1 "111",1 "111"], keep order:false | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t1, index:c(c) range:[3.3000000000,3.3000000000], keep order:false | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t1 keep order:false | ||
| select /*+ use_index_merge(t1 primary, c) */ * from t1 where t1.a = 1 and t1.b = '111' or t1.c = 3.3; | ||
| a b c d | ||
| 1 111 1.1000000000 11 | ||
| 3 333 3.3000000000 13 | ||
| set tidb_enable_clustered_index=default; | ||
| drop table if exists t; | ||
| create table t(pk varbinary(255) NOT NULL, domains json null, image_signatures json null, canonical_links json null, fpi json null, KEY `domains` ((cast(`domains` as char(253) array))), KEY `image_signatures` ((cast(`image_signatures` as char(32) array))),KEY `canonical_links` ((cast(`canonical_links` as char(1000) array))), KEY `fpi` ((cast(`fpi` as unsigned array)))); | ||
| explain format='plan_tree' SELECT pk FROM t WHERE "B2c32" member of (domains) OR "2eoqyp6399" member of (image_signatures) OR "E33EAdAc2Bee.com/s/link" member of (canonical_links) OR json_contains(fpi, '[69236881]') LIMIT 10; | ||
| id task access object operator info | ||
| Projection root planner__core__casetest__index__index.t.pk | ||
| └─IndexMerge root type: union, limit embedded(offset:0, count:10) | ||
| ├─Limit(Build) cop[tikv] offset:0, count:10 | ||
| │ └─IndexRangeScan cop[tikv] table:t, index:domains(cast(`domains` as char(253) array)) range:["B2c32","B2c32"], keep order:false, stats:pseudo | ||
| ├─Limit(Build) cop[tikv] offset:0, count:10 | ||
| │ └─IndexRangeScan cop[tikv] table:t, index:image_signatures(cast(`image_signatures` as char(32) array)) range:["2eoqyp6399","2eoqyp6399"], keep order:false, stats:pseudo | ||
| ├─Limit(Build) cop[tikv] offset:0, count:10 | ||
| │ └─IndexRangeScan cop[tikv] table:t, index:canonical_links(cast(`canonical_links` as char(1000) array)) range:["E33EAdAc2Bee.com/s/link","E33EAdAc2Bee.com/s/link"], keep order:false, stats:pseudo | ||
| ├─Limit(Build) cop[tikv] offset:0, count:10 | ||
| │ └─IndexRangeScan cop[tikv] table:t, index:fpi(cast(`fpi` as unsigned array)) range:[69236881,69236881], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false, stats:pseudo | ||
| drop table if exists t1, t2; | ||
| CREATE TABLE `t1` (`a` varchar(488) COLLATE utf8_general_ci DEFAULT NULL, `b` binary(206) DEFAULT '0', `c` json DEFAULT NULL, UNIQUE KEY `idx_29` (`a`,(cast(`c` as signed array)),`b`), UNIQUE KEY `idx_30` ((cast(`c` as signed array)),`a`(5))) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; | ||
| CREATE TABLE `t2` (`a` float NOT NULL DEFAULT '5217.6055',`b` json NOT NULL,`c` json NOT NULL,`d` varchar(181) COLLATE gbk_bin NOT NULL DEFAULT 'FbVkA~^', KEY `idx_26` (`a`),PRIMARY KEY (`a`,`d`) /*T![clustered_index] NONCLUSTERED */,UNIQUE KEY `idx_28` (`a`,(cast(`b` as binary(64) array)),`d`)) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_bin; | ||
| explain format='plan_tree' SELECT /*+ inl_join(t1)*/ `t2`.`c` AS `r0` FROM `t1` JOIN `t2` ON `t1`.`a`=`t2`.`d` WHERE `t2`.`d`='' AND NOT (6252179388429456273 MEMBER OF (`t1`.`c`)); | ||
| id task access object operator info | ||
| HashJoin root inner join, equal:[eq(planner__core__casetest__index__index.t2.d, planner__core__casetest__index__index.t1.a)] | ||
| ├─TableReader(Build) root data:Selection | ||
| │ └─Selection cop[tikv] eq(planner__core__casetest__index__index.t2.d, "") | ||
| │ └─TableFullScan cop[tikv] table:t2 keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:Selection | ||
| └─Selection cop[tikv] not(isnull(planner__core__casetest__index__index.t1.a)), not(istrue_with_null(json_memberof(cast(6252179388429456273, json BINARY), planner__core__casetest__index__index.t1.c))) | ||
| └─TableFullScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| SHOW WARNINGS; | ||
| Level Code Message | ||
| Warning 1815 Optimizer Hint /*+ INL_JOIN(t1) */ or /*+ TIDB_INLJ(t1) */ is inapplicable | ||
| DROP table if exists t; | ||
| CREATE TABLE `t` (`pk` varbinary(255) NOT NULL,`nslc` json DEFAULT NULL,`fpi` json DEFAULT NULL,`point_of_sale_country` varchar(2) DEFAULT NULL,PRIMARY KEY (`pk`) /*T![clustered_index] CLUSTERED */,KEY `fpi` ((cast(`fpi` as unsigned array))),KEY `nslc` ((cast(`nslc` as char(1000) array)),`point_of_sale_country`),KEY `nslc_old` ((cast(`nslc` as char(1000) array)))); | ||
| explain format='plan_tree' SELECT /*+ use_index_merge(t, fpi, nslc_old, nslc) */ * FROM t WHERE 15975127 member of (fpi) AND "OC8p0106XTkt.org/s/link" member of (nslc) LIMIT 10; | ||
| id task access object operator info | ||
| IndexMerge root type: intersection, limit embedded(offset:0, count:10) | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:fpi(cast(`fpi` as unsigned array)) range:[15975127,15975127], keep order:false, stats:pseudo | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:nslc(cast(`nslc` as char(1000) array), point_of_sale_country) range:["OC8p0106XTkt.org/s/link","OC8p0106XTkt.org/s/link"], keep order:false, stats:pseudo | ||
| └─TableRowIDScan(Probe) cop[tikv] table:t keep order:false, stats:pseudo | ||
| drop table if exists t; | ||
| CREATE TABLE t (nslc json DEFAULT NULL,fpi json DEFAULT NULL,point_of_sale_country int,KEY nslc ((cast(nslc as char(1000) array)),point_of_sale_country),KEY fpi ((cast(fpi as unsigned array)))); | ||
| explain format='plan_tree' SELECT /*+ use_index_merge(t, nslc) */ * FROM t WHERE 57260686 member of (fpi) AND "OC8p1763XTkt.org/s/link" member of (nslc) LIMIT 1; | ||
| id task access object operator info | ||
| Limit root offset:0, count:1 | ||
| └─IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:nslc(cast(`nslc` as char(1000) array), point_of_sale_country) range:["OC8p1763XTkt.org/s/link","OC8p1763XTkt.org/s/link"], keep order:false, stats:pseudo | ||
| └─Limit(Probe) cop[tikv] offset:0, count:1 | ||
| └─Selection cop[tikv] json_memberof(cast(57260686, json BINARY), planner__core__casetest__index__index.t.fpi) | ||
| └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo | ||
| explain format='plan_tree' SELECT /*+ use_index_merge(t, fpi) */ * FROM t WHERE 57260686 member of (fpi) AND "OC8p1763XTkt.org/s/link" member of (nslc) LIMIT 1; | ||
| id task access object operator info | ||
| Limit root offset:0, count:1 | ||
| └─IndexMerge root type: union | ||
| ├─IndexRangeScan(Build) cop[tikv] table:t, index:fpi(cast(`fpi` as unsigned array)) range:[57260686,57260686], keep order:false, stats:pseudo | ||
| └─Limit(Probe) cop[tikv] offset:0, count:1 | ||
| └─Selection cop[tikv] json_memberof(cast("OC8p1763XTkt.org/s/link", json BINARY), planner__core__casetest__index__index.t.nslc) | ||
| └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the corresponding .t test file exists
fd -t f 'index.test' tests/integrationtest/t/planner/core/casetest/index/
# Check if there's documentation about how to record integration tests
if [ -f "docs/agents/testing-flow.md" ]; then
echo "=== Recording instructions from testing-flow.md ==="
rg -A 10 -i "integration test" docs/agents/testing-flow.md || echo "No integration test section found"
fi
# Verify the result file line count matches expected structure
echo "=== Result file statistics ==="
wc -l tests/integrationtest/r/planner/core/casetest/index/index.resultRepository: pingcap/tidb
Length of output: 312
🏁 Script executed:
# Find the actual structure of integrationtest directories
fd -t d 'integrationtest' . | head -20
# Look for the index test file with broader search
find . -name '*index*' -path '*/integrationtest/*' -type f 2>/dev/null | grep -E '\.(t|result)$' | head -20
# Check for the docs directory and testing guidance
ls -la docs/ 2>/dev/null | head -20
# Look for any testing documentation
find . -name '*testing*' -o -name '*TESTING*' 2>/dev/null | grep -v '.git' | head -20Repository: pingcap/tidb
Length of output: 1280
🏁 Script executed:
# Find .t test files (test input files)
find tests/integrationtest/t -name '*.t' -o -name '*.test' 2>/dev/null | head -20
# Look for agents documentation referenced in learnings
find . -path '*/.agents/*' -o -path '*/agents/*' 2>/dev/null | grep -i test | head -20
# Search for the specific testing documentation mentioned in learnings
find . -name 'testing-flow.md' -o -name '*testing*flow*' 2>/dev/null | head -10
# Check if there's a .agents/skills directory
ls -la .agents/skills/ 2>/dev/null || echo "No .agents/skills found"
# List top-level test files in integrationtest
ls -la tests/integrationtest/t/ 2>/dev/null | head -30Repository: pingcap/tidb
Length of output: 3075
🏁 Script executed:
# Search for index.test file in planner/core/casetest directories
find tests/integrationtest/t -path '*planner*' -name '*.test' 2>/dev/null | head -20
# List contents of planner directory if it exists
ls -la tests/integrationtest/t/planner/ 2>/dev/null || echo "No planner directory"
# Search for any documentation about integration tests
rg -l "integration.*test" docs/ 2>/dev/null | head -10
# Look for README or instructions in integrationtest directory
ls -la tests/integrationtest/ 2>/dev/null
# Check if there's any CI/workflow documentation that mentions recording
find . -name '*.md' -path '*docs*' 2>/dev/null | xargs grep -l "integration.*test" 2>/dev/null | head -10Repository: pingcap/tidb
Length of output: 3210
🏁 Script executed:
# Check the casetest directory structure more carefully
ls -la tests/integrationtest/t/planner/core/casetest/ 2>/dev/null
# Look for any integration test that might contain index tests
find tests/integrationtest/t -name '*index*' 2>/dev/null
# Check if the result file directory has a corresponding test file
ls -la tests/integrationtest/r/planner/core/casetest/ 2>/dev/null
# Look at README for integration test instructions
cat tests/integrationtest/README.md 2>/dev/null
# Check the run-tests.sh script to understand test execution
head -100 tests/integrationtest/run-tests.shRepository: pingcap/tidb
Length of output: 7387
🏁 Script executed:
# Check what's in the result index directory
ls -la tests/integrationtest/r/planner/core/casetest/index/ 2>/dev/null
# Search for any test file containing index-related tests in casetest
find tests/integrationtest/t/planner/core/casetest -name '*.test' -exec grep -l "index\|INDEX" {} \; 2>/dev/null | head -10
# Check if index test file exists elsewhere
find tests/integrationtest -name 'index.test' 2>/dev/null
# Look at the integration.test file to see if it contains index tests
head -50 tests/integrationtest/t/planner/core/casetest/integration.test
# Check if there's a planner/core/index subdirectory
find tests/integrationtest/t/planner/core -type d -name 'index*' 2>/dev/nullRepository: pingcap/tidb
Length of output: 3361
🏁 Script executed:
# Verify: the result file exists but check if there's a corresponding .test file
echo "=== Checking for index.test file ==="
[ -f tests/integrationtest/t/planner/core/casetest/index/index.test ] && echo "FOUND: index.test" || echo "NOT FOUND: index.test"
# List all files in both directories
echo ""
echo "=== Files in t/planner/core/casetest/index/ ==="
ls -la tests/integrationtest/t/planner/core/casetest/index/ 2>/dev/null || echo "Directory does not exist"
echo ""
echo "=== Files in r/planner/core/casetest/index/ ==="
ls -la tests/integrationtest/r/planner/core/casetest/index/ 2>/dev/null
# Check git status to see if this is a new file
echo ""
echo "=== Git status of the result file ==="
cd tests/integrationtest && git status r/planner/core/casetest/index/index.result 2>/dev/null || echo "Not in git or no git info"Repository: pingcap/tidb
Length of output: 519
🏁 Script executed:
# Double-check: search for any .test file that might generate this index.result
rg -l "planner.*index" tests/integrationtest/t --type test | head -20
# Check what the README says about the structure
echo "=== Test file discovery ==="
find tests/integrationtest/t -name '*.test' | wc -l
echo "Test files found. Now checking result files:"
find tests/integrationtest/r -name '*.result' | wc -l
echo "Result files found."
# Verify if there's any pairing of test and result files with different paths
echo ""
echo "=== Checking result files without test counterparts ==="
for result in $(find tests/integrationtest/r -name '*.result' -type f); do
testfile="${result/\/r\//\/t\/}"
testfile="${testfile/\.result/.test}"
if [ ! -f "$testfile" ]; then
echo "ORPHAN RESULT: $result (no test file at $testfile)"
fi
done | head -20Repository: pingcap/tidb
Length of output: 1978
🏁 Script executed:
# Verify the structure - check how other result files map to test files
echo "=== Sample of properly paired test/result files ==="
ls tests/integrationtest/t/explain*.test | head -5 | while read f; do
result="${f/\/t\//\/r\/}"
result="${result/\.test/.result}"
echo "Test: $f"
echo "Result: $result"
[ -f "$result" ] && echo "✓ Result exists" || echo "✗ Result missing"
echo ""
done
# Specifically check if index.result should be paired with a test file
echo ""
echo "=== Files in integration test directories ==="
echo "Test count in planner/core/casetest:"
find tests/integrationtest/t/planner/core/casetest -name '*.test' | wc -l
echo ""
echo "Result count in planner/core/casetest:"
find tests/integrationtest/r/planner/core/casetest -name '*.result' | wc -l
# Get file size to understand scale
echo ""
echo "=== File size comparison ==="
echo "index.result size:"
wc -c tests/integrationtest/r/planner/core/casetest/index/index.result
# Check integration.test which seems to be the main test file
echo ""
echo "=== Checking integration.test ==="
wc -l tests/integrationtest/t/planner/core/casetest/integration.test
echo ""
echo "First lines of integration.test:"
head -5 tests/integrationtest/t/planner/core/casetest/integration.testRepository: pingcap/tidb
Length of output: 1240
🏁 Script executed:
# Critical: check if there's a test file that should be generating this result
echo "=== Looking for test files that might generate index.result ==="
rg -l "index" tests/integrationtest/t/planner/core/casetest/*.test 2>/dev/null
# Check the integration.test to see if it contains index-related tests
echo ""
echo "=== Searching for index-related tests in integration.test ==="
rg -c "index|INDEX" tests/integrationtest/t/planner/core/casetest/integration.test
# Check the result files to see which test file they correspond to
echo ""
echo "=== List of result files in casetest ==="
ls -1 tests/integrationtest/r/planner/core/casetest/*.result 2>/dev/null
# Check size of integration.result
echo ""
echo "=== Comparing sizes ==="
echo "integration.result:"
wc -c tests/integrationtest/r/planner/core/casetest/integration.result
echo ""
echo "index.result:"
wc -c tests/integrationtest/r/planner/core/casetest/index/index.resultRepository: pingcap/tidb
Length of output: 919
🏁 Script executed:
# Critical verification: check if there's any indication of where this should come from
echo "=== Checking git log for this file ==="
cd tests/integrationtest && git log --oneline -20 -- r/planner/core/casetest/index/index.result 2>/dev/null || echo "No recent changes"
# Check for any test setup or configuration that might generate this
echo ""
echo "=== Searching for 'index' test references in the codebase ==="
rg "index\.result|index\.test" tests/integrationtest/ --type-list 2>/dev/null | head -20
# Look at the first few lines of the orphaned result to understand its source
echo ""
echo "=== First 30 lines of index.result ==="
head -30 tests/integrationtest/r/planner/core/casetest/index/index.resultRepository: pingcap/tidb
Length of output: 390
🏁 Script executed:
# Trace back: check the git commit that added this file
echo "=== Git commit details ==="
cd tests/integrationtest && git show --stat 3d6e2ebc 2>/dev/null | head -50
# Look for PR reference
echo ""
echo "=== Commit message ==="
cd tests/integrationtest && git log -1 --format="%B" 3d6e2ebc 2>/dev/nullRepository: pingcap/tidb
Length of output: 3043
🏁 Script executed:
# Go back to repo root and check the file directly
echo "=== Current directory ==="
pwd
echo ""
echo "=== Checking if index.result exists ==="
ls -la tests/integrationtest/r/planner/core/casetest/index/index.result 2>/dev/null && echo "FILE EXISTS" || echo "FILE NOT FOUND"
# Try to read the file from repo root
echo ""
echo "=== First 30 lines of index.result ==="
head -30 tests/integrationtest/r/planner/core/casetest/index/index.result 2>/dev/null || echo "Cannot read file"
# Check the git log from repo root
echo ""
echo "=== Git log for index.result ==="
git log --oneline -5 -- tests/integrationtest/r/planner/core/casetest/index/index.result 2>/dev/null
# Check if this is part of the PR being reviewed
echo ""
echo "=== Current HEAD ==="
git log --oneline -1Repository: pingcap/tidb
Length of output: 3391
🏁 Script executed:
# Final verification: check if there's supposed to be a test file
echo "=== Checking PR `#66349` reference ==="
git log --all --grep="66349" --oneline | head -5
echo ""
echo "=== Verify file pairing requirement ==="
# The README says test files in t/ and result files in r/ should be paired
# Check the integration.test size and structure
echo "Integration.test lines:"
wc -l tests/integrationtest/t/planner/core/casetest/integration.test
echo ""
echo "Integration.result lines:"
wc -l tests/integrationtest/r/planner/core/casetest/integration.result
echo ""
echo "=== Checking if index.result content is in integration.test ==="
# Search if the content of index.result is actually in integration.test
rg "drop table if exists t1, t2;" tests/integrationtest/t/planner/core/casetest/integration.test
echo ""
echo "=== Alternative: check if this should be in a different test file ==="
rg "TIDB_INLJ\|IndexMerge" tests/integrationtest/t/planner/core/casetest/*.test | cut -d: -f1 | sort -uRepository: pingcap/tidb
Length of output: 615
🏁 Script executed:
# Final check: verify the actual issue
echo "=== Summary of findings ==="
echo ""
echo "1. Result file exists:"
[ -f tests/integrationtest/r/planner/core/casetest/index/index.result ] && echo " ✓ tests/integrationtest/r/planner/core/casetest/index/index.result (74352 bytes)"
echo ""
echo "2. Test file exists:"
[ -f tests/integrationtest/t/planner/core/casetest/index/index.test ] && echo " ✓ tests/integrationtest/t/planner/core/casetest/index/index.test" || echo " ✗ tests/integrationtest/t/planner/core/casetest/index/index.test NOT FOUND"
echo ""
echo "3. Test/result pairing:"
echo " Expected structure: t/<name>.test → r/<name>.result"
echo " Actual for index: No test file found"
echo ""
echo "4. File origin (from git):"
git show 3d6e2ebc:tests/integrationtest/r/planner/core/casetest/index/index.result 2>/dev/null | wc -l
echo " Lines in commit 3d6e2ebc: $(git show 3d6e2ebc:tests/integrationtest/r/planner/core/casetest/index/index.result 2>/dev/null | wc -l)"
echo ""
echo "5. PR context:"
git log --all --oneline | grep -i "66349" | head -1Repository: pingcap/tidb
Length of output: 544
Missing test file for integration test result.
The result file tests/integrationtest/r/planner/core/casetest/index/index.result (823 lines) has no corresponding test file at tests/integrationtest/t/planner/core/casetest/index/index.test. Integration test result files must be paired with test files to be regenerated via the recording command per the framework's expectations. Add the missing test file or verify this result should be incorporated into an existing test file like tests/integrationtest/t/planner/core/casetest/integration.test.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/integrationtest/r/planner/core/casetest/index/index.result` around
lines 1 - 823, Missing pairing for the integration test result:
tests/integrationtest/r/planner/core/casetest/index/index.result has no
corresponding test input file; either add
tests/integrationtest/t/planner/core/casetest/index/index.test containing the
test SQLs that produced this result or confirm that the result should be merged
into an existing file such as
tests/integrationtest/t/planner/core/casetest/integration.test, then regenerate
the .result by running the framework's recording command so the result and test
file are in sync.
| select * from t t1 left join t t2 on t1.a=t2.a limit 1, 1; | ||
| a a | ||
| 2 2 | ||
| explain format='plan_tree' select * from t t1 left join t t2 on t1.a=t2.a limit 1, 1; | ||
| id task access object operator info | ||
| IndexJoin root left outer join, inner:TableReader, left side:Limit, outer key:test.t.a, inner key:test.t.a, equal cond:eq(test.t.a, test.t.a) | ||
| ├─Limit(Build) root offset:1, count:1 | ||
| │ └─TableReader root data:Limit | ||
| │ └─Limit cop[tikv] offset:0, count:2 | ||
| │ └─TableFullScan cop[tikv] table:t1 keep order:false, stats:pseudo | ||
| └─TableReader(Probe) root data:TableRangeScan | ||
| └─TableRangeScan cop[tikv] table:t2 range: decided by [test.t.a], keep order:false, stats:pseudo |
There was a problem hiding this comment.
Make this LIMIT assertion deterministic.
LIMIT 1, 1 without ORDER BY does not guarantee which row is returned, and the adjacent plan_tree already shows keep order:false. As written, this result can flap when planner/executor behavior changes. Please add an explicit ordering key before recording the expected row.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/integrationtest/r/planner/core/issuetest/planner_issue.result` around
lines 668 - 679, The test's non-deterministic LIMIT usage (the SQL "select *
from t t1 left join t t2 on t1.a=t2.a limit 1, 1" and its corresponding "explain
format='plan_tree' ... limit 1, 1") must include an explicit ORDER BY to
guarantee a stable returned row; update both the query and the EXPLAIN query to
add a deterministic ordering key (for example "ORDER BY t1.a" or a stable
primary key/row id) so the expected row assertion is deterministic and matches
the recorded plan output.
|
@ti-chi-bot: The following tests failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
This is an automated cherry-pick of #66349
What problem does this PR solve?
Issue Number: close #63887
Problem Summary:
What changed and how does it work?
Check List
Tests
Side effects
Documentation
Release note
Please refer to Release Notes Language Style Guide to write a quality release note.
Summary by CodeRabbit
New Features
Tests