SERVER-113717 Enumerate INLJ plans in bottom-up enumerator (#44869)

GitOrigin-RevId: ee2d00b5184756d618c1732423367abcd0edfbed
This commit is contained in:
Alya Carina Berciu 2025-12-15 17:12:20 +01:00 committed by MongoDB Bot
parent 55a2afb898
commit 1e14d732b9
17 changed files with 5727 additions and 89 deletions

View File

@ -816,6 +816,113 @@ Execution Engine: sbe
```
usedJoinOptimization: true
### With bottom-up plan enumeration and indexes
### Pipeline
```json
[
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
}
]
```
### Results
```json
{ "_id" : 1, "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "_id" : 1, "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "2312073BEBB7A5E1754E2D03D9CB40B75C126DBF60867D4639E7E281C9E3DFAC",
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStages" : [
{
"inputStages" : [
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
{
"inputStage" : {
"indexName" : "a_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"a" : 1
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"a = a"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "x",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"inputStage" : {
"indexName" : "b_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"b" : 1
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"b = b"
],
"leftEmbeddingField" : "none",
"planNodeId" : 7,
"rightEmbeddingField" : "y",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
}
}
```
## 2. Basic example with two joins and suffix
### No join opt
### Pipeline
@ -1891,6 +1998,143 @@ Execution Engine: sbe
```
usedJoinOptimization: true
### With bottom-up plan enumeration and indexes
### Pipeline
```json
[
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
},
{
"$sortByCount" : "$y.b"
}
]
```
### Results
```json
{ "_id" : "bar", "count" : 6 }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "8E0DF14D3DA69D8B1CF0361E869369F9C5E5F2F2A5D81998E6800BDA4F997B57",
"stages" : [
{
"$cursor" : {
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStages" : [
{
"inputStages" : [
{
"inputStage" : {
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : false,
"a" : true,
"b" : true
}
},
{
"inputStage" : {
"indexName" : "a_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"a" : 1
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"a = a"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "x",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"inputStage" : {
"indexName" : "b_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"b" : 1
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"b = b"
],
"leftEmbeddingField" : "none",
"planNodeId" : 8,
"rightEmbeddingField" : "y",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
}
}
},
{
"$group" : {
"$willBeMerged" : false,
"_id" : "$y.b",
"count" : {
"$sum" : {
"$const" : 1
}
}
}
},
{
"$sort" : {
"sortKey" : {
"count" : -1
}
}
}
]
}
```
## 3. Basic example with referencing field from previous lookup
### No join opt
### Pipeline
@ -2683,6 +2927,103 @@ Execution Engine: sbe
```
usedJoinOptimization: true
### With bottom-up plan enumeration and indexes
### Pipeline
```json
[
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign3",
"as" : "z",
"localField" : "x.c",
"foreignField" : "c"
}
},
{
"$unwind" : "$z"
}
]
```
### Results
```json
{ "_id" : 0, "a" : 1, "b" : "foo", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "z" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 } }
{ "_id" : 1, "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "z" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "z" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "z" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "DC5CAF413E9B6B8B5E8B2D3FB91E4D546524BF2707BBE30472D5CA8E12F6637E",
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStages" : [
{
"inputStages" : [
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
{
"inputStage" : {
"indexName" : "a_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"a" : 1
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"a = a"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "x",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md_foreign3",
"stage" : "COLLSCAN"
}
],
"joinPredicates" : [
"x.c = c"
],
"leftEmbeddingField" : "none",
"planNodeId" : 6,
"rightEmbeddingField" : "z",
"stage" : "HASH_JOIN_EMBEDDING"
}
}
```
## 4. Basic example with 3 joins & subsequent join referencing fields from previous lookups
### No join opt
### Pipeline
@ -3753,6 +4094,142 @@ Execution Engine: sbe
```
usedJoinOptimization: true
### With bottom-up plan enumeration and indexes
### Pipeline
```json
[
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign3",
"as" : "z",
"localField" : "x.c",
"foreignField" : "c"
}
},
{
"$unwind" : "$z"
}
]
```
### Results
```json
{ "_id" : 1, "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 }, "z" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 } }
{ "_id" : 1, "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 }, "z" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 }, "z" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 }, "z" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 }, "z" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 } }
{ "_id" : 2, "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 }, "z" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "DD24FFAE1F16C6C1F5B03939E0C642A797120A2585058C72554C9649BEA427AB",
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStages" : [
{
"inputStages" : [
{
"inputStages" : [
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
{
"inputStage" : {
"indexName" : "a_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"a" : 1
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"a = a"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "x",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"inputStage" : {
"indexName" : "b_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"b" : 1
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"b = b"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "y",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md_foreign3",
"stage" : "COLLSCAN"
}
],
"joinPredicates" : [
"x.c = c"
],
"leftEmbeddingField" : "none",
"planNodeId" : 9,
"rightEmbeddingField" : "z",
"stage" : "HASH_JOIN_EMBEDDING"
}
}
```
## 5. Basic example with 3 joins & subsequent join referencing nested paths
### No join opt
### Pipeline
@ -4770,3 +5247,126 @@ Execution Engine: sbe
```
usedJoinOptimization: true
### With bottom-up plan enumeration and indexes
### Pipeline
```json
[
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign3",
"as" : "w.y",
"localField" : "x.c",
"foreignField" : "c"
}
},
{
"$unwind" : "$w.y"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "k.y.z",
"localField" : "w.y.d",
"foreignField" : "d"
}
},
{
"$unwind" : "$k.y.z"
}
]
```
### Results
```json
{ "_id" : 2, "a" : 2, "b" : "bar", "k" : { "y" : { "z" : { "_id" : 0, "b" : "bar", "d" : 2 } } }, "w" : { "y" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 } }, "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "9226A4FAA10C8790B27D9B5B865D46D0C58179A837F014E6289A34A6EBE76420",
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStages" : [
{
"inputStages" : [
{
"inputStages" : [
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
{
"inputStage" : {
"indexName" : "a_1",
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"a" : 1
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "INDEX_PROBE_NODE"
},
"nss" : "test.basic_joins_md_foreign1",
"stage" : "FETCH"
}
],
"joinPredicates" : [
"a = a"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "x",
"stage" : "INDEXED_NESTED_LOOP_JOIN_EMBEDDING"
},
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md_foreign3",
"stage" : "COLLSCAN"
}
],
"joinPredicates" : [
"x.c = c"
],
"leftEmbeddingField" : "none",
"rightEmbeddingField" : "w.y",
"stage" : "HASH_JOIN_EMBEDDING"
},
{
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md_foreign2",
"stage" : "COLLSCAN"
}
],
"joinPredicates" : [
"w.y.d = d"
],
"leftEmbeddingField" : "none",
"planNodeId" : 8,
"rightEmbeddingField" : "k.y.z",
"stage" : "HASH_JOIN_EMBEDDING"
}
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -139,21 +139,41 @@ function runBasicJoinTest(pipeline) {
outputAggregationPlanAndResults(coll, pipeline, {}, true, false, true /* noLineBreak*/);
const seed420HJResults = coll.aggregate(pipeline).toArray();
verifyExplainOutput(pipeline, true /* joinOptExpectedInExplainOutput */);
assert(
_resultSetsEqualUnordered(noJoinOptResults, seed420HJResults),
"Results differ between no join opt and seed 420 HJ",
);
assert.commandWorked(db.adminCommand({setParameter: 1, internalRandomJoinReorderDefaultToHashJoin: false}));
foreignColl1.createIndex({a: 1});
foreignColl2.createIndex({b: 1});
subSection("With fixed order, index join");
outputAggregationPlanAndResults(coll, pipeline, {}, true, false, true /* noLineBreak*/);
const seedINJResults = coll.aggregate(pipeline).toArray();
verifyExplainOutput(pipeline, true /* joinOptExpectedInExplainOutput */);
const seedINLJResults = coll.aggregate(pipeline).toArray();
assert(
_resultSetsEqualUnordered(noJoinOptResults, seedINLJResults),
"Results differ between no join opt and INLJ",
);
subSection("With bottom-up plan enumeration and indexes");
assert.commandWorked(
db.adminCommand({
setParameter: 1,
internalJoinReorderMode: "bottomUp",
internalJoinPlanTreeShape: "leftDeep",
}),
);
outputAggregationPlanAndResults(coll, pipeline, {}, true, false);
const bottomUpINLJResults = coll.aggregate(pipeline).toArray();
assert(
_resultSetsEqualUnordered(noJoinOptResults, bottomUpINLJResults),
"Results differ between no join opt and INLJ",
);
foreignColl1.dropIndex({a: 1});
foreignColl2.dropIndex({b: 1});
assert(
_resultSetsEqualUnordered(noJoinOptResults, seedINJResults),
"Results differ between no join opt and INJ",
);
} finally {
// Reset flags.
assert.commandWorked(db.adminCommand({setParameter: 1, internalEnableJoinOptimization: false}));

View File

@ -106,7 +106,7 @@ bool isAggEligibleForJoinReordering(const MultipleCollectionAccessor& mca,
return AggJoinModel::pipelineEligibleForJoinReordering(pipeline);
}
bool indexIsValidForINLJ(std::shared_ptr<const IndexCatalogEntry> ice) {
bool indexIsValidForINLJ(const std::shared_ptr<const IndexCatalogEntry>& ice) {
auto desc = ice->descriptor();
return !desc->isHashedIdIndex() && !desc->hidden() && !desc->isPartial() && !desc->isSparse() &&
desc->collation().isEmpty() && !dynamic_cast<WildcardAccessMethod*>(ice->accessMethod());
@ -121,8 +121,7 @@ AvailableIndexes extractINLJEligibleIndexes(const QuerySolutionMap& solns,
AvailableIndexes perCollIdxs;
for (const auto& [cq, _] : solns) {
const auto& ns = cq->nss();
auto it = perCollIdxs.find(ns);
if (it != perCollIdxs.end()) {
if (perCollIdxs.contains(ns)) {
// We've already pre-processed this collection's indexes.
continue;
}

View File

@ -31,10 +31,21 @@
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/query/compiler/optimizer/join/join_graph.h"
#include "mongo/db/query/compiler/physical_model/query_solution/query_solution.h"
#include "mongo/util/str.h"
namespace mongo::join_ordering {
namespace {
NodeSet getBitset(const JoinPlanNode& node) {
return std::visit(
OverloadedVisitor{[](const JoiningNode& join) { return join.bitset; },
[](const INLJRHSNode& ip) { return NodeSet().set(ip.node); },
[](const BaseNode& base) {
return NodeSet().set(base.node);
}},
node);
}
/**
* Helper to pretty-print NodeSet.
*/
@ -57,20 +68,29 @@ std::string joinMethodToString(JoinMethod method) {
MONGO_UNREACHABLE_TASSERT(11336901);
}
std::string joinNodeStringPrefix(const JoinPlanNode& node,
size_t numNodesToPrint,
std::string indentStr) {
return str::stream() << indentStr << "[" << nodeSetToString(getBitset(node), numNodesToPrint)
<< "]";
}
} // namespace
std::string BaseNode::toString(size_t numNodesToPrint, std::string indentStr) const {
return str::stream() << indentStr << "[" << nodeSetToString(bitset, numNodesToPrint) << "]["
return str::stream() << joinNodeStringPrefix(*this, numNodesToPrint, indentStr) << "["
<< nss.toString_forTest() << "] " << soln->summaryString();
}
std::string JoiningNode::toString(size_t numNodesToPrint, std::string indentStr) const {
auto ss = std::stringstream() << indentStr << "[" << nodeSetToString(bitset, numNodesToPrint)
<< "] ";
const auto methodStr = joinMethodToString(method);
std::string INLJRHSNode::toString(size_t numNodesToPrint, std::string indentStr) const {
return str::stream() << joinNodeStringPrefix(*this, numNodesToPrint, indentStr) << "["
<< nss.toString_forTest() << "] INDEX_PROBE "
<< entry->descriptor()->keyPattern();
}
ss << methodStr;
return ss.str();
std::string JoiningNode::toString(size_t numNodesToPrint, std::string indentStr) const {
return str::stream() << joinNodeStringPrefix(*this, numNodesToPrint, indentStr) << " "
<< joinMethodToString(method);
}
std::string JoinSubset::toString(size_t numNodesToPrint) const {
@ -91,11 +111,19 @@ JoinPlanNodeId JoinPlanNodeRegistry::registerJoinNode(const JoinSubset& subset,
return id;
}
JoinPlanNodeId JoinPlanNodeRegistry::registerBaseNode(const JoinSubset& subset,
JoinPlanNodeId JoinPlanNodeRegistry::registerBaseNode(NodeId node,
const QuerySolution* soln,
const NamespaceString& nss) {
tassert(11371702, "Expected an initialized qsn", soln);
JoinPlanNodeId id = _allJoinPlans.size();
_allJoinPlans.emplace_back(BaseNode{soln, nss, subset.subset});
_allJoinPlans.emplace_back(BaseNode{soln, nss, node});
return id;
}
JoinPlanNodeId JoinPlanNodeRegistry::registerINLJRHSNode(
NodeId node, std::shared_ptr<const IndexCatalogEntry> entry, const NamespaceString& nss) {
JoinPlanNodeId id = _allJoinPlans.size();
_allJoinPlans.emplace_back(INLJRHSNode{std::move(entry), nss, node});
return id;
}
@ -108,6 +136,9 @@ std::string JoinPlanNodeRegistry::joinPlanNodeToString(JoinPlanNodeId nodeId,
[&indentStr, &ss, numNodesToPrint](const BaseNode& n) {
ss << n.toString(numNodesToPrint, indentStr);
},
[&indentStr, &ss, numNodesToPrint](const INLJRHSNode& n) {
ss << n.toString(numNodesToPrint, indentStr);
},
[this, &indentStr, &ss, numNodesToPrint](const JoiningNode& n) {
std::string nxtIndent = str::stream() << indentStr + " ";
ss << n.toString(numNodesToPrint, indentStr) << "\n"
@ -135,24 +166,34 @@ std::string JoinPlanNodeRegistry::joinPlansToString(const JoinPlans& plans,
}
NodeSet JoinPlanNodeRegistry::getBitset(JoinPlanNodeId id) const {
return std::visit([](const auto& n) { return n.bitset; }, get(id));
return std::visit(
OverloadedVisitor{[](const JoiningNode& join) { return join.bitset; },
[](const INLJRHSNode& ip) { return NodeSet().set(ip.node); },
[](const BaseNode& base) {
return NodeSet().set(base.node);
}},
get(id));
}
BSONObj JoinPlanNodeRegistry::joinPlanNodeToBSON(JoinPlanNodeId nodeId,
size_t numNodesToPrint) const {
return std::visit(
OverloadedVisitor{[this, numNodesToPrint](const JoiningNode& join) {
return BSON("subset"
<< nodeSetToString(join.bitset, numNodesToPrint)
<< "method" << joinMethodToString(join.method) << "left"
<< joinPlanNodeToBSON(join.left, numNodesToPrint)
<< "right"
<< joinPlanNodeToBSON(join.right, numNodesToPrint));
},
[numNodesToPrint](const BaseNode& base) {
return BSON("subset" << nodeSetToString(base.bitset, numNodesToPrint)
<< "accessPath" << base.soln->summaryString());
}},
OverloadedVisitor{
[this, numNodesToPrint](const JoiningNode& join) {
return BSON("subset" << nodeSetToString(join.bitset, numNodesToPrint) << "method"
<< joinMethodToString(join.method) << "left"
<< joinPlanNodeToBSON(join.left, numNodesToPrint) << "right"
<< joinPlanNodeToBSON(join.right, numNodesToPrint));
},
[numNodesToPrint](const INLJRHSNode& ip) {
return BSON("subset" << nodeSetToString(ip.node, numNodesToPrint) << "accessPath"
<< (str::stream() << "INDEX_PROBE "
<< ip.entry->descriptor()->keyPattern()));
},
[numNodesToPrint](const BaseNode& base) {
return BSON("subset" << nodeSetToString(base.node, numNodesToPrint) << "accessPath"
<< base.soln->summaryString());
}},
get(nodeId));
}

View File

@ -31,6 +31,7 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/compiler/optimizer/join/join_graph.h"
#include "mongo/db/query/compiler/physical_model/query_solution/query_solution.h"
#include "mongo/db/query/util/bitset_util.h"
#include "mongo/util/modules.h"
#include <variant>
@ -40,9 +41,10 @@ namespace mongo::join_ordering {
// Forward declarations.
struct JoiningNode;
struct BaseNode;
struct INLJRHSNode;
using JoinPlanNodeId = size_t;
using JoinPlanNode = std::variant<JoiningNode, BaseNode>;
using JoinPlanNode = std::variant<JoiningNode, BaseNode, INLJRHSNode>;
using JoinPlans = std::vector<JoinPlanNodeId>;
/**
@ -64,6 +66,11 @@ struct JoinSubset {
return subset.count() == 1;
}
NodeId getNodeId() const {
tassert(11371703, "Must have just one node", isBaseCollectionAccess());
return (NodeId)*begin(subset);
}
inline JoinPlanNodeId bestPlan() const {
tassert(11336908, "Expected bestPlanIndex < plans.size()", bestPlanIndex < plans.size());
return plans[bestPlanIndex];
@ -99,11 +106,29 @@ enum class JoinMethod {
struct BaseNode {
// Pointer to best access path obtained by CBR.
const QuerySolution* soln;
// Namespace this access path corresponds to.
const NamespaceString& nss;
// Keeps a copy of the bitset representing the subgraph this originated from.
const NodeSet bitset;
// Corresponds to node in the graph this represents a base table access to.
const NodeId node;
std::string toString(size_t numNodesToPrint = kMaxNodesInJoin,
std::string indentStr = "") const;
};
/**
* A JoinPlan node representing the right hand side of a INLJ join (an index probe).
*/
struct INLJRHSNode {
// The index that will be used to construct an IndexProbe QSN.
std::shared_ptr<const IndexCatalogEntry> entry;
// Namespace this access path corresponds to.
const NamespaceString& nss;
// Corresponds to node in the graph this represents a base table access to.
const NodeId node;
std::string toString(size_t numNodesToPrint = kMaxNodesInJoin,
std::string indentStr = "") const;
@ -146,9 +171,12 @@ public:
JoinMethod method,
JoinPlanNodeId left,
JoinPlanNodeId right);
JoinPlanNodeId registerBaseNode(const JoinSubset& subset,
JoinPlanNodeId registerBaseNode(NodeId node,
const QuerySolution* soln,
const NamespaceString& nss);
JoinPlanNodeId registerINLJRHSNode(NodeId node,
std::shared_ptr<const IndexCatalogEntry> entry,
const NamespaceString& nss);
const JoinPlanNode& get(JoinPlanNodeId id) const {
return _allJoinPlans[id];

View File

@ -34,8 +34,6 @@
#include "mongo/db/query/compiler/optimizer/join/plan_enumerator_helpers.h"
#include "mongo/logv2/log.h"
#include <sstream>
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
namespace mongo::join_ordering {
@ -52,9 +50,10 @@ bool PlanEnumeratorContext::canPlanBeEnumerated(PlanTreeShape type,
const JoinSubset& left,
const JoinSubset& right,
const JoinSubset& subset) const {
if (method == JoinMethod::NLJ && !right.isBaseCollectionAccess()) {
// NLJ plans perform poorly when the right hand side is not a collection access- don't
// enumerate this plan.
if ((method == JoinMethod::NLJ || method == JoinMethod::INLJ) &&
!right.isBaseCollectionAccess()) {
// NLJ plans perform poorly when the right hand side is not a collection access, while INLJ
// requires the right side to be a base table access. Don't enumerate this plan.
return false;
}
@ -103,8 +102,8 @@ bool PlanEnumeratorContext::canPlanBeEnumerated(PlanTreeShape type,
*/
const auto& leftJoin = _registry.getAs<JoiningNode>(left.bestPlan());
return _registry.isOfType<JoiningNode>(leftJoin.right) ||
(_registry.isOfType<BaseNode>(leftJoin.right) &&
_registry.isOfType<BaseNode>(leftJoin.left));
(!_registry.isOfType<JoiningNode>(leftJoin.right) &&
!_registry.isOfType<JoiningNode>(leftJoin.left));
} else if (!right.isBaseCollectionAccess()) {
/**
@ -126,8 +125,8 @@ bool PlanEnumeratorContext::canPlanBeEnumerated(PlanTreeShape type,
*/
const auto& rightJoin = _registry.getAs<JoiningNode>(right.bestPlan());
return _registry.isOfType<JoiningNode>(rightJoin.left) ||
(_registry.isOfType<BaseNode>(rightJoin.right) &&
_registry.isOfType<BaseNode>(rightJoin.left));
(!_registry.isOfType<JoiningNode>(rightJoin.right) &&
!_registry.isOfType<JoiningNode>(rightJoin.left));
}
break;
@ -147,9 +146,27 @@ void PlanEnumeratorContext::addJoinPlan(PlanTreeShape type,
if (!canPlanBeEnumerated(type, method, left, right, subset)) {
return;
}
// TODO SERVER-113059: Rudimentary cost metric/tracking.
subset.plans.push_back(
_registry.registerJoinNode(subset, method, left.bestPlan(), right.bestPlan()));
if (method == JoinMethod::INLJ) {
// TODO SERVER-115093: Change this to an equality tassert once we break cycles.
tassert(11371701, "Expected at least one edge", edges.size() >= 1);
auto edge = edges[0];
auto ie = bestIndexSatisfyingJoinPredicates(
_ctx, (NodeId)*begin(right.subset), _ctx.joinGraph.getEdge(edge));
if (ie) {
const auto nodeId = right.getNodeId();
const auto& nss = _ctx.joinGraph.accessPathAt(nodeId)->nss();
auto rhs = _registry.registerINLJRHSNode(nodeId, ie, nss);
subset.plans.push_back(
_registry.registerJoinNode(subset, method, left.bestPlan(), rhs));
} else {
return;
}
} else {
// TODO SERVER-113059: Rudimentary cost metric/tracking.
subset.plans.push_back(
_registry.registerJoinNode(subset, method, left.bestPlan(), right.bestPlan()));
}
LOGV2_DEBUG(11336912,
5,
@ -179,6 +196,7 @@ void PlanEnumeratorContext::enumerateJoinPlans(PlanTreeShape type,
return;
}
addJoinPlan(type, JoinMethod::INLJ, left, right, joinEdges, cur);
addJoinPlan(type, JoinMethod::HJ, left, right, joinEdges, cur);
addJoinPlan(type, JoinMethod::NLJ, left, right, joinEdges, cur);
}
@ -198,12 +216,12 @@ void PlanEnumeratorContext::enumerateJoinSubsets(PlanTreeShape type) {
const auto* qsn = _ctx.cbrCqQsns.at(cq).get();
_joinSubsets[kBaseLevel].push_back(JoinSubset(NodeSet{}.set(i)));
_joinSubsets[kBaseLevel].back().plans = {
_registry.registerBaseNode(_joinSubsets[kBaseLevel].back(), qsn, cq->nss())};
_registry.registerBaseNode((NodeId)i, qsn, cq->nss())};
}
// Initialize the rest of the joinSubsets.
for (int level = 1; level < numNodes; ++level) {
const auto& joinSubsetsPrevLevel = _joinSubsets[level - 1];
auto& joinSubsetsPrevLevel = _joinSubsets[level - 1];
auto& joinSubsetsCurrLevel = _joinSubsets[level];
// Preallocate entries for all subsets in the current level.
joinSubsetsCurrLevel.reserve(cs.next());

View File

@ -85,7 +85,7 @@ DEATH_TEST(PlanEnumeratorHelpersDeathTest, TooManyInvocationsOfCombinationSequen
class JoinPlanEnumeratorTest : public JoinOrderingTestFixture {
public:
void initGraph(size_t numNodes) {
void initGraph(size_t numNodes, bool withIndexes = false) {
for (size_t i = 0; i < numNodes; i++) {
auto nss =
NamespaceString::createNamespaceString_forTest("test", str::stream() << "nss" << i);
@ -95,19 +95,27 @@ public:
jCtx.cbrCqQsns.insert(
{cq.get(), makeCollScanPlan(nss, cq->getPrimaryMatchExpression()->clone())});
ASSERT_TRUE(graph.addNode(nss, std::move(cq), boost::none).has_value());
if (withIndexes) {
jCtx.perCollIdxs.emplace(
nss, makeIndexCatalogEntries({BSON(fieldName << (i % 2 ? 1 : -1))}));
}
resolvedPaths.emplace_back(ResolvedPath{(NodeId)i, FieldPath(fieldName)});
}
}
void testLargeSubset(unittest::GoldenTestContext* goldenCtx,
PlanTreeShape shape,
size_t numNodes) {
initGraph(numNodes);
size_t numNodes,
bool withIndexes = false) {
initGraph(numNodes, withIndexes);
for (size_t i = 1; i < numNodes; ++i) {
// Make the graph fully connected in order to ensure we generate as many plans as
// possible.
for (size_t j = 0; j < i; ++j) {
ASSERT_TRUE(graph.addSimpleEqualityEdge((NodeId)j, (NodeId)i, 0, 1).has_value());
ASSERT_TRUE(graph.addSimpleEqualityEdge((NodeId)j, (NodeId)i, j, i).has_value());
}
}
@ -290,16 +298,31 @@ TEST_F(JoinPlanEnumeratorTest, LeftDeep8Nodes) {
testLargeSubset(&goldenCtx, PlanTreeShape::LEFT_DEEP, 8);
}
TEST_F(JoinPlanEnumeratorTest, LeftDeep8NodesINLJ) {
unittest::GoldenTestContext goldenCtx(&goldenTestConfig);
testLargeSubset(&goldenCtx, PlanTreeShape::LEFT_DEEP, 8, true /* withIndexes */);
}
TEST_F(JoinPlanEnumeratorTest, RightDeep8Nodes) {
unittest::GoldenTestContext goldenCtx(&goldenTestConfig);
testLargeSubset(&goldenCtx, PlanTreeShape::RIGHT_DEEP, 8);
}
TEST_F(JoinPlanEnumeratorTest, RightDeep8NodesINLJ) {
unittest::GoldenTestContext goldenCtx(&goldenTestConfig);
testLargeSubset(&goldenCtx, PlanTreeShape::RIGHT_DEEP, 8, true /* withIndexes */);
}
TEST_F(JoinPlanEnumeratorTest, ZigZag8Nodes) {
unittest::GoldenTestContext goldenCtx(&goldenTestConfig);
testLargeSubset(&goldenCtx, PlanTreeShape::ZIG_ZAG, 8);
}
TEST_F(JoinPlanEnumeratorTest, ZigZag8NodesINLJ) {
unittest::GoldenTestContext goldenCtx(&goldenTestConfig);
testLargeSubset(&goldenCtx, PlanTreeShape::ZIG_ZAG, 8, true /* withIndexes */);
}
TEST_F(JoinPlanEnumeratorTest, InitialzeLargeSubsets) {
testLargeSubset(nullptr /* No golden test here. */, PlanTreeShape::LEFT_DEEP, 15);
}

View File

@ -30,6 +30,7 @@
#include "mongo/db/query/compiler/optimizer/join/reorder_joins.h"
#include "mongo/db/query/compiler/optimizer/join/join_graph.h"
#include "mongo/db/query/compiler/optimizer/join/join_plan.h"
#include "mongo/db/query/compiler/optimizer/join/join_reordering_context.h"
#include "mongo/db/query/compiler/optimizer/join/plan_enumerator.h"
#include "mongo/db/query/compiler/optimizer/join/plan_enumerator_helpers.h"
@ -173,6 +174,10 @@ std::unique_ptr<QuerySolutionNode> buildQSNFromJoinPlan(const JoinReorderingCont
[&qsn](const BaseNode& base) {
// TODO SERVER-111913: Avoid this clone
qsn = base.soln->root()->clone();
},
[&ctx, &qsn, &registry](const INLJRHSNode& ip) {
qsn = createIndexProbeQSN(ctx.joinGraph.getNode(ip.node),
ip.entry);
}},
registry.get(nodeId));
return qsn;
@ -187,9 +192,11 @@ NodeId getLeftmostNodeIdOfJoinPlan(const JoinReorderingContext& ctx,
return getLeftmostNodeIdOfJoinPlan(
ctx, join.left, registry);
},
[](const BaseNode& base) {
BitsetIterator<kMaxNodesInJoin> it = begin(base.bitset);
return (NodeId)*it;
[](const BaseNode& base) { return base.node; },
[](const INLJRHSNode&) {
// By definition, this should never happen.
MONGO_UNREACHABLE_TASSERT(11371704);
return (NodeId)0;
}},
registry.get(nodeId));
}

View File

@ -35,9 +35,6 @@
#include "mongo/db/query/compiler/optimizer/join/join_reordering_context.h"
#include "mongo/db/query/compiler/optimizer/join/plan_enumerator_helpers.h"
#include "mongo/db/query/compiler/optimizer/join/unit_test_helpers.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry_mock.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_mock.h"
#include "mongo/unittest/golden_test.h"
#include "mongo/unittest/unittest.h"
@ -55,36 +52,6 @@ QuerySolutionMap cloneSolnMap(const QuerySolutionMap& qsm) {
return ret;
}
IndexDescriptor makeIndexDescriptor(BSONObj indexSpec) {
IndexSpec spec;
spec.version(2).name("name").addKeys(indexSpec);
return IndexDescriptor(IndexNames::BTREE, spec.toBSON());
}
IndexCatalogEntryMock makeIndexCatalogEntry(BSONObj indexSpec) {
return IndexCatalogEntryMock{nullptr /*opCtx*/,
CollectionPtr{},
"" /*ident*/,
makeIndexDescriptor(indexSpec),
false /*isFrozen*/};
}
IndexCatalogMock makeIndexCatalog(const std::vector<BSONObj>& keyPatterns) {
IndexCatalogMock catalog;
for (auto&& kp : keyPatterns) {
catalog.createIndexEntry(nullptr, nullptr, makeIndexDescriptor(kp), {});
}
return catalog;
}
std::vector<std::shared_ptr<const IndexCatalogEntry>> makeIndexCatalogEntries(
const std::vector<BSONObj>& keyPatterns) {
auto ic = makeIndexCatalog(keyPatterns);
std::vector<std::shared_ptr<const IndexCatalogEntry>> idxs(keyPatterns.size());
return ic.getEntriesShared(IndexCatalog::InclusionPolicy::kReady);
}
class ReorderGraphTest : public JoinOrderingTestFixture {
protected:
// Helper struct for intializing a namespace, its embedding, any residual filter, and indexes

View File

@ -36,6 +36,35 @@ namespace mongo::join_ordering {
using namespace cost_based_ranker;
IndexDescriptor makeIndexDescriptor(BSONObj indexSpec) {
IndexSpec spec;
spec.version(2).name("name").addKeys(indexSpec);
return IndexDescriptor(IndexNames::BTREE, spec.toBSON());
}
IndexCatalogEntryMock makeIndexCatalogEntry(BSONObj indexSpec) {
return IndexCatalogEntryMock{nullptr /*opCtx*/,
CollectionPtr{},
"" /*ident*/,
makeIndexDescriptor(indexSpec),
false /*isFrozen*/};
}
IndexCatalogMock makeIndexCatalog(const std::vector<BSONObj>& keyPatterns) {
IndexCatalogMock catalog;
for (auto&& kp : keyPatterns) {
catalog.createIndexEntry(nullptr, nullptr, makeIndexDescriptor(kp), {});
}
return catalog;
}
std::vector<std::shared_ptr<const IndexCatalogEntry>> makeIndexCatalogEntries(
const std::vector<BSONObj>& keyPatterns) {
auto ic = makeIndexCatalog(keyPatterns);
std::vector<std::shared_ptr<const IndexCatalogEntry>> idxs(keyPatterns.size());
return ic.getEntriesShared(IndexCatalog::InclusionPolicy::kReady);
}
MultipleCollectionAccessor multipleCollectionAccessor(OperationContext* opCtx,
std::vector<NamespaceString> namespaces) {
auto mainAcquisitionReq = CollectionAcquisitionRequest::fromOpCtx(

View File

@ -35,6 +35,9 @@
#include "mongo/db/query/compiler/optimizer/join/join_reordering_context.h"
#include "mongo/db/query/multiple_collection_accessor.h"
#include "mongo/db/shard_role/shard_catalog/catalog_test_fixture.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry_mock.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_mock.h"
#include "mongo/unittest/golden_test_base.h"
#include "mongo/util/modules.h"
@ -46,6 +49,16 @@ namespace mongo::join_ordering {
MultipleCollectionAccessor multipleCollectionAccessor(OperationContext* opCtx,
std::vector<NamespaceString> namespaces);
/**
* Helpers used to mock index information for unit tests without requiring a
* MultipleCollectionAccessor.
*/
IndexDescriptor makeIndexDescriptor(BSONObj indexSpec);
IndexCatalogEntryMock makeIndexCatalogEntry(BSONObj indexSpec);
IndexCatalogMock makeIndexCatalog(const std::vector<BSONObj>& keyPatterns);
std::vector<std::shared_ptr<const IndexCatalogEntry>> makeIndexCatalogEntries(
const std::vector<BSONObj>& keyPatterns);
/**
* Text fixture with helpful functions for manipulating the catalog, constructing samples and
* queries/QSNs.

View File

@ -0,0 +1,738 @@
Level 0:
00000001, 00000010, 00000100, 00001000, 00010000, 00100000, 01000000, 10000000
Level 1:
00000011, 00000101, 00001001, 00010001, 00100001, 01000001, 10000001, 00000110, 00001010, 00010010, 00100010, 01000010, 10000010, 00001100, 00010100, 00100100, 01000100, 10000100, 00011000, 00101000, 01001000, 10001000, 00110000, 01010000, 10010000, 01100000, 10100000, 11000000
Level 2:
00000111, 00001011, 00010011, 00100011, 01000011, 10000011, 00001101, 00010101, 00100101, 01000101, 10000101, 00011001, 00101001, 01001001, 10001001, 00110001, 01010001, 10010001, 01100001, 10100001, 11000001, 00001110, 00010110, 00100110, 01000110, 10000110, 00011010, 00101010, 01001010, 10001010, 00110010, 01010010, 10010010, 01100010, 10100010, 11000010, 00011100, 00101100, 01001100, 10001100, 00110100, 01010100, 10010100, 01100100, 10100100, 11000100, 00111000, 01011000, 10011000, 01101000, 10101000, 11001000, 01110000, 10110000, 11010000, 11100000
Level 3:
00001111, 00010111, 00100111, 01000111, 10000111, 00011011, 00101011, 01001011, 10001011, 00110011, 01010011, 10010011, 01100011, 10100011, 11000011, 00011101, 00101101, 01001101, 10001101, 00110101, 01010101, 10010101, 01100101, 10100101, 11000101, 00111001, 01011001, 10011001, 01101001, 10101001, 11001001, 01110001, 10110001, 11010001, 11100001, 00011110, 00101110, 01001110, 10001110, 00110110, 01010110, 10010110, 01100110, 10100110, 11000110, 00111010, 01011010, 10011010, 01101010, 10101010, 11001010, 01110010, 10110010, 11010010, 11100010, 00111100, 01011100, 10011100, 01101100, 10101100, 11001100, 01110100, 10110100, 11010100, 11100100, 01111000, 10111000, 11011000, 11101000, 11110000
Level 4:
00011111, 00101111, 01001111, 10001111, 00110111, 01010111, 10010111, 01100111, 10100111, 11000111, 00111011, 01011011, 10011011, 01101011, 10101011, 11001011, 01110011, 10110011, 11010011, 11100011, 00111101, 01011101, 10011101, 01101101, 10101101, 11001101, 01110101, 10110101, 11010101, 11100101, 01111001, 10111001, 11011001, 11101001, 11110001, 00111110, 01011110, 10011110, 01101110, 10101110, 11001110, 01110110, 10110110, 11010110, 11100110, 01111010, 10111010, 11011010, 11101010, 11110010, 01111100, 10111100, 11011100, 11101100, 11110100, 11111000
Level 5:
00111111, 01011111, 10011111, 01101111, 10101111, 11001111, 01110111, 10110111, 11010111, 11100111, 01111011, 10111011, 11011011, 11101011, 11110011, 01111101, 10111101, 11011101, 11101101, 11110101, 11111001, 01111110, 10111110, 11011110, 11101110, 11110110, 11111010, 11111100
Level 6:
01111111, 10111111, 11011111, 11101111, 11110111, 11111011, 11111101, 11111110
Level 7:
11111111
Output plans (best plan 0):
0.[11111111] INLJ
-> left:
[01111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
1.[11111111] HJ
-> left:
[01111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] COLLSCAN
2.[11111111] NLJ
-> left:
[01111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] COLLSCAN
3.[11111111] INLJ
-> left:
[10111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
4.[11111111] HJ
-> left:
[10111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[01000000][test.nss6] COLLSCAN
5.[11111111] NLJ
-> left:
[10111111] INLJ
-> left:
[00111111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[01000000][test.nss6] COLLSCAN
6.[11111111] INLJ
-> left:
[11011111] INLJ
-> left:
[01011111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
7.[11111111] HJ
-> left:
[11011111] INLJ
-> left:
[01011111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00100000][test.nss5] COLLSCAN
8.[11111111] NLJ
-> left:
[11011111] INLJ
-> left:
[01011111] INLJ
-> left:
[00011111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00100000][test.nss5] COLLSCAN
9.[11111111] INLJ
-> left:
[11101111] INLJ
-> left:
[01101111] INLJ
-> left:
[00101111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
10.[11111111] HJ
-> left:
[11101111] INLJ
-> left:
[01101111] INLJ
-> left:
[00101111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00010000][test.nss4] COLLSCAN
11.[11111111] NLJ
-> left:
[11101111] INLJ
-> left:
[01101111] INLJ
-> left:
[00101111] INLJ
-> left:
[00001111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00010000][test.nss4] COLLSCAN
12.[11111111] INLJ
-> left:
[11110111] INLJ
-> left:
[01110111] INLJ
-> left:
[00110111] INLJ
-> left:
[00010111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
13.[11111111] HJ
-> left:
[11110111] INLJ
-> left:
[01110111] INLJ
-> left:
[00110111] INLJ
-> left:
[00010111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00001000][test.nss3] COLLSCAN
14.[11111111] NLJ
-> left:
[11110111] INLJ
-> left:
[01110111] INLJ
-> left:
[00110111] INLJ
-> left:
[00010111] INLJ
-> left:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00001000][test.nss3] COLLSCAN
15.[11111111] INLJ
-> left:
[11111011] INLJ
-> left:
[01111011] INLJ
-> left:
[00111011] INLJ
-> left:
[00011011] INLJ
-> left:
[00001011] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
16.[11111111] HJ
-> left:
[11111011] INLJ
-> left:
[01111011] INLJ
-> left:
[00111011] INLJ
-> left:
[00011011] INLJ
-> left:
[00001011] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000100][test.nss2] COLLSCAN
17.[11111111] NLJ
-> left:
[11111011] INLJ
-> left:
[01111011] INLJ
-> left:
[00111011] INLJ
-> left:
[00011011] INLJ
-> left:
[00001011] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000100][test.nss2] COLLSCAN
18.[11111111] INLJ
-> left:
[11111101] INLJ
-> left:
[01111101] INLJ
-> left:
[00111101] INLJ
-> left:
[00011101] INLJ
-> left:
[00001101] INLJ
-> left:
[00000101] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
19.[11111111] HJ
-> left:
[11111101] INLJ
-> left:
[01111101] INLJ
-> left:
[00111101] INLJ
-> left:
[00011101] INLJ
-> left:
[00001101] INLJ
-> left:
[00000101] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000010][test.nss1] COLLSCAN
20.[11111111] NLJ
-> left:
[11111101] INLJ
-> left:
[01111101] INLJ
-> left:
[00111101] INLJ
-> left:
[00011101] INLJ
-> left:
[00001101] INLJ
-> left:
[00000101] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000010][test.nss1] COLLSCAN
21.[11111111] INLJ
-> left:
[11111110] INLJ
-> left:
[01111110] INLJ
-> left:
[00111110] INLJ
-> left:
[00011110] INLJ
-> left:
[00001110] INLJ
-> left:
[00000110] INLJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000001][test.nss0] INDEX_PROBE { a0: -1 }
22.[11111111] HJ
-> left:
[11111110] INLJ
-> left:
[01111110] INLJ
-> left:
[00111110] INLJ
-> left:
[00011110] INLJ
-> left:
[00001110] INLJ
-> left:
[00000110] INLJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000001][test.nss0] COLLSCAN
23.[11111111] NLJ
-> left:
[11111110] INLJ
-> left:
[01111110] INLJ
-> left:
[00111110] INLJ
-> left:
[00011110] INLJ
-> left:
[00001110] INLJ
-> left:
[00000110] INLJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
-> right:
[00000001][test.nss0] COLLSCAN

View File

@ -0,0 +1,258 @@
Level 0:
00000001, 00000010, 00000100, 00001000, 00010000, 00100000, 01000000, 10000000
Level 1:
00000011, 00000101, 00001001, 00010001, 00100001, 01000001, 10000001, 00000110, 00001010, 00010010, 00100010, 01000010, 10000010, 00001100, 00010100, 00100100, 01000100, 10000100, 00011000, 00101000, 01001000, 10001000, 00110000, 01010000, 10010000, 01100000, 10100000, 11000000
Level 2:
00000111, 00001011, 00010011, 00100011, 01000011, 10000011, 00001101, 00010101, 00100101, 01000101, 10000101, 00011001, 00101001, 01001001, 10001001, 00110001, 01010001, 10010001, 01100001, 10100001, 11000001, 00001110, 00010110, 00100110, 01000110, 10000110, 00011010, 00101010, 01001010, 10001010, 00110010, 01010010, 10010010, 01100010, 10100010, 11000010, 00011100, 00101100, 01001100, 10001100, 00110100, 01010100, 10010100, 01100100, 10100100, 11000100, 00111000, 01011000, 10011000, 01101000, 10101000, 11001000, 01110000, 10110000, 11010000, 11100000
Level 3:
00001111, 00010111, 00100111, 01000111, 10000111, 00011011, 00101011, 01001011, 10001011, 00110011, 01010011, 10010011, 01100011, 10100011, 11000011, 00011101, 00101101, 01001101, 10001101, 00110101, 01010101, 10010101, 01100101, 10100101, 11000101, 00111001, 01011001, 10011001, 01101001, 10101001, 11001001, 01110001, 10110001, 11010001, 11100001, 00011110, 00101110, 01001110, 10001110, 00110110, 01010110, 10010110, 01100110, 10100110, 11000110, 00111010, 01011010, 10011010, 01101010, 10101010, 11001010, 01110010, 10110010, 11010010, 11100010, 00111100, 01011100, 10011100, 01101100, 10101100, 11001100, 01110100, 10110100, 11010100, 11100100, 01111000, 10111000, 11011000, 11101000, 11110000
Level 4:
00011111, 00101111, 01001111, 10001111, 00110111, 01010111, 10010111, 01100111, 10100111, 11000111, 00111011, 01011011, 10011011, 01101011, 10101011, 11001011, 01110011, 10110011, 11010011, 11100011, 00111101, 01011101, 10011101, 01101101, 10101101, 11001101, 01110101, 10110101, 11010101, 11100101, 01111001, 10111001, 11011001, 11101001, 11110001, 00111110, 01011110, 10011110, 01101110, 10101110, 11001110, 01110110, 10110110, 11010110, 11100110, 01111010, 10111010, 11011010, 11101010, 11110010, 01111100, 10111100, 11011100, 11101100, 11110100, 11111000
Level 5:
00111111, 01011111, 10011111, 01101111, 10101111, 11001111, 01110111, 10110111, 11010111, 11100111, 01111011, 10111011, 11011011, 11101011, 11110011, 01111101, 10111101, 11011101, 11101101, 11110101, 11111001, 01111110, 10111110, 11011110, 11101110, 11110110, 11111010, 11111100
Level 6:
01111111, 10111111, 11011111, 11101111, 11110111, 11111011, 11111101, 11111110
Level 7:
11111111
Output plans (best plan 0):
0.[11111111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01111111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
1.[11111111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[10111111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[00111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
2.[11111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[11011111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01011111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00011111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
3.[11111111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[11101111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01101111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00101111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
4.[11111111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[11110111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01110111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00110111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00010111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00000111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
5.[11111111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[11111011] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01111011] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111011] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011011] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001011] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
6.[11111111] HJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[11111101] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01111101] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111101] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011101] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001101] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000101] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
7.[11111111] HJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[11111110] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01111110] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111110] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011110] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001110] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000110] INLJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }

View File

@ -0,0 +1,258 @@
Level 0:
00000001, 00000010, 00000100, 00001000, 00010000, 00100000, 01000000, 10000000
Level 1:
00000011, 00000101, 00001001, 00010001, 00100001, 01000001, 10000001, 00000110, 00001010, 00010010, 00100010, 01000010, 10000010, 00001100, 00010100, 00100100, 01000100, 10000100, 00011000, 00101000, 01001000, 10001000, 00110000, 01010000, 10010000, 01100000, 10100000, 11000000
Level 2:
00000111, 00001011, 00010011, 00100011, 01000011, 10000011, 00001101, 00010101, 00100101, 01000101, 10000101, 00011001, 00101001, 01001001, 10001001, 00110001, 01010001, 10010001, 01100001, 10100001, 11000001, 00001110, 00010110, 00100110, 01000110, 10000110, 00011010, 00101010, 01001010, 10001010, 00110010, 01010010, 10010010, 01100010, 10100010, 11000010, 00011100, 00101100, 01001100, 10001100, 00110100, 01010100, 10010100, 01100100, 10100100, 11000100, 00111000, 01011000, 10011000, 01101000, 10101000, 11001000, 01110000, 10110000, 11010000, 11100000
Level 3:
00001111, 00010111, 00100111, 01000111, 10000111, 00011011, 00101011, 01001011, 10001011, 00110011, 01010011, 10010011, 01100011, 10100011, 11000011, 00011101, 00101101, 01001101, 10001101, 00110101, 01010101, 10010101, 01100101, 10100101, 11000101, 00111001, 01011001, 10011001, 01101001, 10101001, 11001001, 01110001, 10110001, 11010001, 11100001, 00011110, 00101110, 01001110, 10001110, 00110110, 01010110, 10010110, 01100110, 10100110, 11000110, 00111010, 01011010, 10011010, 01101010, 10101010, 11001010, 01110010, 10110010, 11010010, 11100010, 00111100, 01011100, 10011100, 01101100, 10101100, 11001100, 01110100, 10110100, 11010100, 11100100, 01111000, 10111000, 11011000, 11101000, 11110000
Level 4:
00011111, 00101111, 01001111, 10001111, 00110111, 01010111, 10010111, 01100111, 10100111, 11000111, 00111011, 01011011, 10011011, 01101011, 10101011, 11001011, 01110011, 10110011, 11010011, 11100011, 00111101, 01011101, 10011101, 01101101, 10101101, 11001101, 01110101, 10110101, 11010101, 11100101, 01111001, 10111001, 11011001, 11101001, 11110001, 00111110, 01011110, 10011110, 01101110, 10101110, 11001110, 01110110, 10110110, 11010110, 11100110, 01111010, 10111010, 11011010, 11101010, 11110010, 01111100, 10111100, 11011100, 11101100, 11110100, 11111000
Level 5:
00111111, 01011111, 10011111, 01101111, 10101111, 11001111, 01110111, 10110111, 11010111, 11100111, 01111011, 10111011, 11011011, 11101011, 11110011, 01111101, 10111101, 11011101, 11101101, 11110101, 11111001, 01111110, 10111110, 11011110, 11101110, 11110110, 11111010, 11111100
Level 6:
01111111, 10111111, 11011111, 11101111, 11110111, 11111011, 11111101, 11111110
Level 7:
11111111
Output plans (best plan 0):
0.[11111111] HJ
-> left:
[10000000][test.nss7] COLLSCAN
-> right:
[01111111] INLJ
-> left:
[00111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011111] INLJ
-> left:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[01000000][test.nss6] INDEX_PROBE { a6: -1 }
1.[11111111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[10111111] INLJ
-> left:
[00111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[00011111] INLJ
-> left:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
2.[11111111] HJ
-> left:
[00100000][test.nss5] COLLSCAN
-> right:
[11011111] INLJ
-> left:
[01011111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00011111] INLJ
-> left:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00010000][test.nss4] INDEX_PROBE { a4: -1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
3.[11111111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[11101111] INLJ
-> left:
[01101111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00101111] INLJ
-> left:
[00001111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
4.[11111111] HJ
-> left:
[00001000][test.nss3] COLLSCAN
-> right:
[11110111] INLJ
-> left:
[01110111] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00110111] INLJ
-> left:
[00010111] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00000111] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
5.[11111111] HJ
-> left:
[00000100][test.nss2] COLLSCAN
-> right:
[11111011] INLJ
-> left:
[01111011] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111011] INLJ
-> left:
[00011011] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001011] INLJ
-> left:
[00000011] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000010][test.nss1] INDEX_PROBE { a1: 1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
6.[11111111] HJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[11111101] INLJ
-> left:
[01111101] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111101] INLJ
-> left:
[00011101] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001101] INLJ
-> left:
[00000101] INLJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }
7.[11111111] HJ
-> left:
[00000001][test.nss0] COLLSCAN
-> right:
[11111110] INLJ
-> left:
[01111110] HJ
-> left:
[01000000][test.nss6] COLLSCAN
-> right:
[00111110] INLJ
-> left:
[00011110] HJ
-> left:
[00010000][test.nss4] COLLSCAN
-> right:
[00001110] INLJ
-> left:
[00000110] INLJ
-> left:
[00000010][test.nss1] COLLSCAN
-> right:
[00000100][test.nss2] INDEX_PROBE { a2: -1 }
-> right:
[00001000][test.nss3] INDEX_PROBE { a3: 1 }
-> right:
[00100000][test.nss5] INDEX_PROBE { a5: 1 }
-> right:
[10000000][test.nss7] INDEX_PROBE { a7: 1 }