Skip to content

Commit 01b562a

Browse files
pashandor789KirillKurdyukov
authored andcommitted
[CBO] Make hints process many trees. (ydb-platform#8554)
1 parent d01478d commit 01b562a

8 files changed

Lines changed: 91 additions & 25 deletions

ydb/core/kqp/ut/join/data/queries/join_order_hints_complex.sql

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
PRAGMA TablePathPrefix='/Root';
22

3-
PRAGMA ydb.OptJoinOrderHints='[ ["R", "S"], ["T", "U"] ]';
3+
PRAGMA ydb.OptJoinOrderHints=
4+
'[
5+
[["R", "S"], ["T", "U"]]
6+
]';
7+
48
PRAGMA ydb.OptCardinalityHints =
59
'[
610
{"labels":["R"], "op":"#", "value":10e8},
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
PRAGMA TablePathPrefix='/Root';
2+
3+
PRAGMA ydb.OptJoinOrderHints=
4+
'[
5+
["R", "S"],
6+
["T", "U"]
7+
]';
8+
PRAGMA ydb.OptCardinalityHints =
9+
'[
10+
{"labels":["R"], "op":"#", "value":10e8},
11+
{"labels":["T"], "op":"#", "value":1},
12+
{"labels":["R", "T"], "op":"#", "value":1},
13+
{"labels":["R", "S"], "op":"#", "value":10e8},
14+
{"labels":["T", "U"], "op":"#", "value":10e8},
15+
{"labels":["V"], "op":"#", "value":1}
16+
]';
17+
18+
SELECT * FROM
19+
R INNER JOIN S on R.id = S.id
20+
INNER JOIN T on R.id = T.id
21+
INNER JOIN U on T.id = U.id
22+
INNER JOIN V on U.id = V.id;

ydb/core/kqp/ut/join/data/queries/join_order_hints_simple.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ PRAGMA ydb.OptCardinalityHints =
88
{"labels":["R", "T"], "op":"#", "value":1},
99
{"labels":["R", "S"], "op":"#", "value":10e8}
1010
]';
11-
PRAGMA ydb.OptJoinOrderHints='[ "T", ["R", "S"] ]';
11+
PRAGMA ydb.OptJoinOrderHints=
12+
'[
13+
["T", ["R", "S"]]
14+
]';
1215

1316
SELECT * FROM
1417
R INNER JOIN S on R.id = S.id

ydb/core/kqp/ut/join/kqp_join_order_ut.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,39 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
377377
UNIT_ASSERT_VALUES_EQUAL(GetJoinOrder(plan).GetStringRobust(), R"([[["R","S"],["T","U"]],"V"])") ;
378378
}
379379

380+
void TraverseJoinTree(
381+
const NJson::TJsonValue& tree,
382+
std::function<void(const NJson::TJsonValue&, const NJson::TJsonValue&)> joinVisitFunc
383+
) {
384+
if (tree.IsArray()) {
385+
auto join = tree.GetArray();
386+
Y_ENSURE(join.size() == 2, Sprintf("Incorrect join tree piece, expected 2 children for join, got : %s", tree.GetStringRobust().c_str()));
387+
joinVisitFunc(join[0], join[1]);
388+
TraverseJoinTree(join[0], joinVisitFunc);
389+
TraverseJoinTree(join[1], joinVisitFunc);
390+
} else if (tree.IsString()) {
391+
} else {
392+
Y_ENSURE(false, Sprintf("Incorrect join tree piece: %s", tree.GetStringRobust().c_str()));
393+
}
394+
}
395+
396+
Y_UNIT_TEST_XOR_OR_BOTH_FALSE(TestJoinOrderHintsManyHintTrees, StreamLookupJoin, ColumnStore) {
397+
auto plan = ExecuteJoinOrderTestDataQueryWithStats("queries/join_order_hints_many_hint_trees.sql", "stats/basic.json", StreamLookupJoin, ColumnStore);
398+
auto joinOrder = GetJoinOrder(plan);
399+
400+
THashSet<TString> expectedJoins = { "R, S", "T, U" };
401+
TraverseJoinTree(
402+
joinOrder,
403+
[&expectedJoins](const NJson::TJsonValue& joinLhs, const NJson::TJsonValue& joinRhs){
404+
if (joinLhs.IsString() && joinRhs.IsString()) {
405+
expectedJoins.erase(Sprintf("%s, %s", joinLhs.GetString().c_str(), joinRhs.GetString().c_str()));
406+
}
407+
}
408+
);
409+
410+
UNIT_ASSERT_C(expectedJoins.empty(), Sprintf("%s wasn't found in '%s' join order!", JoinSeq(", ", expectedJoins).c_str(), joinOrder.GetStringRobust().c_str()));
411+
}
412+
380413
void JoinOrderTestWithOverridenStats(const TString& queryPath, const TString& statsPath, TString correctJoinOrderPath, bool useStreamLookupJoin, bool useColumnStore
381414
) {
382415
auto kikimr = GetKikimrWithJoinSettings(useStreamLookupJoin, GetStatic(statsPath));

ydb/library/yql/core/cbo/cbo_optimizer_new.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,16 @@ std::shared_ptr<IBaseOptimizerNode> MakeJoinTreeFromJson(const NJson::TJsonValue
330330
}
331331

332332
TJoinOrderHints::TJoinOrderHints(const TString& json) {
333+
const static TString PARSING_FORMAT_ERROR =
334+
R"(Join order hints parsing failed. The example of the format: [ ["A", "B"], ["B", "C"] ])";
335+
333336
NJson::TJsonValue jsonTree;
334337
NJson::ReadJsonTree(json, &jsonTree, true);
335-
HintsTree = MakeJoinTreeFromJson(jsonTree);
338+
339+
Y_ENSURE(jsonTree.IsArray(), PARSING_FORMAT_ERROR);
340+
for (const auto& hintTreeJson: jsonTree.GetArray()) {
341+
HintTrees.push_back(MakeJoinTreeFromJson(hintTreeJson));
342+
}
336343
}
337344

338345
TJoinAlgoHints::TJoinAlgoHints(const TString& json) {

ydb/library/yql/core/cbo/cbo_optimizer_new.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct TJoinAlgoHints {
109109
};
110110

111111
struct TJoinOrderHints {
112-
std::shared_ptr<IBaseOptimizerNode> HintsTree;
112+
TVector<std::shared_ptr<IBaseOptimizerNode>> HintTrees;
113113

114114
TJoinOrderHints() {}
115115
TJoinOrderHints(const TString& json);

ydb/library/yql/dq/opt/dq_opt_join_hypergraph.h

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
#include <numeric>
5+
#include <util/string/join.h>
56
#include <util/string/printf.h>
67
#include "bitset.h"
78

@@ -272,21 +273,23 @@ class TJoinOrderHintsApplier {
272273
{}
273274

274275
void Apply(const TJoinOrderHints& hints) {
275-
auto labels = ApplyHintsToSubgraph(hints.HintsTree);
276-
auto nodes = Graph_.GetNodesByRelNames(labels);
277-
278-
for (size_t i = 0; i < Graph_.GetEdges().size(); ++i) {
279-
TNodeSet newLeft = Graph_.GetEdge(i).Left;
280-
if (Overlaps(Graph_.GetEdge(i).Left, nodes) && !IsSubset(Graph_.GetEdge(i).Right, nodes)) {
281-
newLeft |= nodes;
282-
}
276+
for (const auto& hintTree: hints.HintTrees) {
277+
auto labels = ApplyHintsToSubgraph(hintTree);
278+
auto nodes = Graph_.GetNodesByRelNames(labels);
279+
280+
for (size_t i = 0; i < Graph_.GetEdges().size(); ++i) {
281+
TNodeSet newLeft = Graph_.GetEdge(i).Left;
282+
if (Overlaps(Graph_.GetEdge(i).Left, nodes) && !IsSubset(Graph_.GetEdge(i).Right, nodes)) {
283+
newLeft |= nodes;
284+
}
283285

284-
TNodeSet newRight = Graph_.GetEdge(i).Right;
285-
if (Overlaps(Graph_.GetEdge(i).Right, nodes) && !IsSubset(Graph_.GetEdge(i).Left, nodes)) {
286-
newRight |= nodes;
287-
}
286+
TNodeSet newRight = Graph_.GetEdge(i).Right;
287+
if (Overlaps(Graph_.GetEdge(i).Right, nodes) && !IsSubset(Graph_.GetEdge(i).Left, nodes)) {
288+
newRight |= nodes;
289+
}
288290

289-
Graph_.UpdateEdgeSides(i, newLeft, newRight);
291+
Graph_.UpdateEdgeSides(i, newLeft, newRight);
292+
}
290293
}
291294
}
292295

@@ -302,14 +305,8 @@ class TJoinOrderHintsApplier {
302305

303306
auto* maybeEdge = Graph_.FindEdgeBetween(lhs, rhs);
304307
if (maybeEdge == nullptr) {
305-
auto str = [](const TVector<TString>& v) -> TString {
306-
TString s;
307-
for (auto& el : v) { s += (el + ", "); }
308-
return s.empty()? s: s.substr(0, s.length() - 2);
309-
};
310-
311308
const char* errStr = "There is no edge between {%s}, {%s}. The graf: %s";
312-
Y_ENSURE(false, Sprintf(errStr, str(lhsLabels).c_str(), str(rhsLabels).c_str(), Graph_.String().c_str()));
309+
Y_ENSURE(false, Sprintf(errStr, JoinSeq(", ", lhsLabels).c_str(), JoinSeq(", ", rhsLabels).c_str(), Graph_.String().c_str()));
313310
}
314311

315312
size_t revEdgeIdx = maybeEdge->ReversedEdgeId;

ydb/library/yql/dq/opt/dq_opt_make_join_hypergraph.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ TJoinHypergraph<TNodeSet> MakeJoinHypergraph(
100100
YQL_CLOG(TRACE, CoreDq) << graph.String();
101101
}
102102

103-
if (hints.HintsTree != nullptr) {
103+
if (!hints.HintTrees.empty()) {
104104
TJoinOrderHintsApplier joinHints(graph);
105105
joinHints.Apply(hints);
106106
if (NYql::NLog::YqlLogger().NeedToLog(NYql::NLog::EComponent::CoreDq, NYql::NLog::ELevel::TRACE)) {

0 commit comments

Comments
 (0)