Skip to content

Commit 42cceac

Browse files
authored
LookupJoin visualization in SVG (for DS) (#15789)
1 parent b516bba commit 42cceac

File tree

1 file changed

+64
-28
lines changed

1 file changed

+64
-28
lines changed

ydb/public/lib/ydb_cli/common/plan2svg.cpp

+64-28
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,23 @@ TSingleMetric::TSingleMetric(std::shared_ptr<TSummaryMetric> summary, ui64 value
357357
Summary->Add(Details.Sum);
358358
}
359359

360+
TString ParseColumns(const NJson::TJsonValue* node) {
361+
TStringBuilder builder;
362+
builder << '(';
363+
if (node) {
364+
bool firstColumn = true;
365+
for (const auto& subNode : node->GetArray()) {
366+
if (firstColumn) {
367+
firstColumn = false;
368+
} else {
369+
builder << ", ";
370+
}
371+
builder << subNode.GetStringSafe();
372+
}
373+
}
374+
builder << ')';
375+
return builder;
376+
}
360377

361378
void TPlan::Load(const NJson::TJsonValue& node) {
362379
if (auto* subplanNameNode = node.GetValueByPath("Subplan Name")) {
@@ -514,10 +531,16 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
514531
TStringBuilder builder;
515532

516533
if (name == "Iterator" || name == "Member" || name == "ToFlow") {
534+
if (auto* referenceNode = subNode.GetValueByPath(name)) {
535+
auto referenceName = referenceNode->GetStringSafe();
536+
references.insert(referenceName);
537+
info = referenceName;
538+
auto cteRef = "CTE " + referenceName;
539+
auto stageCopy = stage;
540+
MemberRefs.emplace_back(cteRef, std::make_pair<std::shared_ptr<TStage>, ui32>(std::move(stageCopy), stage->Operators.size()));
541+
}
517542
name = "Reference";
518-
}
519-
520-
if (name == "Limit") {
543+
} else if (name == "Limit") {
521544
if (auto* limitNode = subNode.GetValueByPath("Limit")) {
522545
info = limitNode->GetStringSafe();
523546
}
@@ -644,19 +667,7 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
644667
}
645668
builder << table;
646669
}
647-
builder << "(";
648-
if (auto* readColumnsNode = subNode.GetValueByPath("ReadColumns")) {
649-
bool firstColumn = true;
650-
for (const auto& subNode : readColumnsNode->GetArray()) {
651-
if (firstColumn) {
652-
firstColumn = false;
653-
} else {
654-
builder << ", ";
655-
}
656-
builder << subNode.GetStringSafe();
657-
}
658-
}
659-
builder << ")";
670+
builder << ParseColumns(subNode.GetValueByPath("ReadColumns"));
660671
info = builder;
661672
externalOperator = true;
662673
} else if (name == "TopSort" || name == "Top") {
@@ -677,23 +688,14 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
677688
}
678689
}
679690
info = builder;
680-
} else if (name == "Reference") {
681-
if (auto* referenceNode = subNode.GetValueByPath(name)) {
682-
auto referenceName = referenceNode->GetStringSafe();
683-
references.insert(referenceName);
684-
info = referenceName;
685-
auto cteRef = "CTE " + referenceName;
686-
auto stageCopy = stage;
687-
MemberRefs.emplace_back(cteRef, std::make_pair<std::shared_ptr<TStage>, ui32>(std::move(stageCopy), stage->Operators.size()));
688-
}
689691
} else if (name.Contains("Join")) {
690692
operatorType = "Join";
691693
if (auto* conditionNode = subNode.GetValueByPath("Condition")) {
692694
info = conditionNode->GetStringSafe();
693695
}
694696
}
695697

696-
if (externalOperator) {
698+
if (externalOperator && !stage->External) {
697699
externalOperators.emplace_back(name, info);
698700
externalOperators.back().Estimations = GetEstimation(subNode);
699701
} else {
@@ -789,7 +791,7 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
789791
}
790792
}
791793

792-
if (!externalOperators.empty()) {
794+
if (!externalOperators.empty() && !stage->External) {
793795
auto connection = std::make_shared<TConnection>(*stage, "External", 0);
794796
stage->Connections.push_back(connection);
795797
Stages.push_back(std::make_shared<TStage>("External"));
@@ -853,7 +855,8 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
853855
if (auto* outputNode = stage->StatsNode->GetValueByPath("Output")) {
854856
for (const auto& subNode : outputNode->GetArray()) {
855857
if (auto* nameNode = subNode.GetValueByPath("Name")) {
856-
if (ToString(parentPlanNodeId) == nameNode->GetStringSafe()) {
858+
auto name = nameNode->GetStringSafe();
859+
if (name == ToString(parentPlanNodeId) || name == "RESULT") {
857860
if (auto* popNode = subNode.GetValueByPath("Pop")) {
858861
if (auto* bytesNode = popNode->GetValueByPath("Bytes")) {
859862
stage->OutputBytes = std::make_shared<TSingleMetric>(OutputBytes,
@@ -917,6 +920,26 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
917920
}
918921

919922
if (planNodeType == "Connection") {
923+
if (subNodeType == "TableLookup") {
924+
// "TableLookup" => "Table" + "Lookup"
925+
auto connection = std::make_shared<TConnection>(*stage, "Lookup", stage->PlanNodeId);
926+
stage->Connections.push_back(connection);
927+
Stages.push_back(std::make_shared<TStage>("External"));
928+
connection->FromStage = Stages.back();
929+
Stages.back()->External = true;
930+
TStringBuilder builder;
931+
if (auto* tableNode = plan.GetValueByPath("Table")) {
932+
auto table = tableNode->GetStringSafe();
933+
auto n = table.find_last_of('/');
934+
if (n != table.npos) {
935+
table = table.substr(n + 1);
936+
}
937+
builder << table;
938+
}
939+
builder << ParseColumns(plan.GetValueByPath("Columns")) << " by " << ParseColumns(plan.GetValueByPath("LookupKeyColumns"));
940+
Stages.back()->Operators.emplace_back("TableLookup", builder);
941+
subNodeType = "Table";
942+
}
920943
auto* keyColumnsNode = plan.GetValueByPath("KeyColumns");
921944
auto* sortColumnsNode = plan.GetValueByPath("SortColumns");
922945
if (auto* subNode = plan.GetValueByPath("Plans")) {
@@ -1027,6 +1050,17 @@ void TPlan::LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& no
10271050
}
10281051
}
10291052
LoadSource(plan, stage->Operators, ingressRowsNode);
1053+
} else if (subNodeType == "TableFullScan") {
1054+
if (stage->IngressName) {
1055+
ythrow yexception() << "Plan stage already has Ingress [" << stage->IngressName << "]";
1056+
}
1057+
stage->IngressName = subNodeType;
1058+
auto connection = std::make_shared<TConnection>(*stage, "External", 0);
1059+
stage->Connections.push_back(connection);
1060+
Stages.push_back(std::make_shared<TStage>("External"));
1061+
connection->FromStage = Stages.back();
1062+
Stages.back()->External = true;
1063+
LoadStage(Stages.back(), plan, stage->PlanNodeId);
10301064
} else {
10311065
stage->Connections.push_back(std::make_shared<TConnection>(*stage, "Implicit", stage->PlanNodeId));
10321066
Stages.push_back(std::make_shared<TStage>(subNodeType));
@@ -1822,6 +1856,8 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS
18221856
else if (c->NodeType == "UnionAll") mark = "U";
18231857
else if (c->NodeType == "Broadcast") mark = "B";
18241858
else if (c->NodeType == "External") mark = "E";
1859+
else if (c->NodeType == "Table") mark = "T";
1860+
else if (c->NodeType == "Lookup") mark = "L";
18251861
else mark = "?";
18261862

18271863
canvas

0 commit comments

Comments
 (0)