Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ydb/core/kqp/opt/kqp_opt.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct TKqpOptimizeContext : public TSimpleRefCount<TKqpOptimizeContext> {
std::shared_ptr<NJson::TJsonValue> OverrideStatistics{};
std::shared_ptr<NYql::TCardinalityHints> CardinalityHints{};
std::shared_ptr<NYql::TJoinAlgoHints> JoinAlgoHints{};
std::shared_ptr<NYql::TJoinOrderHints> JoinOrderHints{};

std::shared_ptr<NJson::TJsonValue> GetOverrideStatistics() {
if (Config->OptOverrideStatistics.Get()) {
Expand Down Expand Up @@ -70,6 +71,17 @@ struct TKqpOptimizeContext : public TSimpleRefCount<TKqpOptimizeContext> {
}
}

NYql::TJoinOrderHints GetJoinOrderHints() {
if (Config->OptJoinOrderHints.Get()) {
if (!JoinOrderHints) {
JoinOrderHints = std::make_shared<NYql::TJoinOrderHints>(*Config->OptJoinOrderHints.Get());
}
return *JoinOrderHints;
} else {
return NYql::TJoinOrderHints();
}
}

bool IsDataQuery() const {
return QueryCtx->Type == NYql::EKikimrQueryType::Dml;
}
Expand Down
8 changes: 6 additions & 2 deletions ydb/core/kqp/opt/logical/kqp_opt_log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,12 @@ class TKqpLogicalOptTransformer : public TOptimizeTransformerBase {
rels.emplace_back(std::make_shared<TKqpRelOptimizerNode>(TString(label), stat, node));
},
KqpCtx.EquiJoinsCount,
KqpCtx.GetCardinalityHints(),
KqpCtx.GetJoinAlgoHints());
TOptimizerHints{
.CardinalityHints = KqpCtx.GetCardinalityHints(),
.JoinAlgoHints = KqpCtx.GetJoinAlgoHints(),
.JoinOrderHints = KqpCtx.GetJoinOrderHints()
}
);
DumpAppliedRule("OptimizeEquiJoinWithCosts", node.Ptr(), output.Ptr(), ctx);
return output;
}
Expand Down
1 change: 1 addition & 0 deletions ydb/core/kqp/provider/yql_kikimr_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ TKikimrConfiguration::TKikimrConfiguration() {
REGISTER_SETTING(*this, OptOverrideStatistics);
REGISTER_SETTING(*this, OptCardinalityHints);
REGISTER_SETTING(*this, OptJoinAlgoHints);
REGISTER_SETTING(*this, OptJoinOrderHints);
REGISTER_SETTING(*this, OverridePlanner);
REGISTER_SETTING(*this, UseGraceJoinCoreForMap);

Expand Down
1 change: 1 addition & 0 deletions ydb/core/kqp/provider/yql_kikimr_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct TKikimrSettings {
NCommon::TConfSetting<TString, false> OptOverrideStatistics;
NCommon::TConfSetting<TString, false> OptCardinalityHints;
NCommon::TConfSetting<TString, false> OptJoinAlgoHints;
NCommon::TConfSetting<TString, false> OptJoinOrderHints;

/* Disable optimizer rules */
NCommon::TConfSetting<bool, false> OptDisableTopSort;
Expand Down
33 changes: 28 additions & 5 deletions ydb/core/kqp/ut/common/kqp_ut_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,7 @@ bool JoinOrderAndAlgosMatch(const TString& optimized, const TString& reference){
}

/* Temporary solution to canonize tests */
NJson::TJsonValue CanonizeJoinOrderImpl(const NJson::TJsonValue& opt) {
NJson::TJsonValue GetDetailedJoinOrderImpl(const NJson::TJsonValue& opt) {
NJson::TJsonValue res;

auto op = opt.GetMapSafe().at("Operators").GetArraySafe()[0];
Expand All @@ -1417,18 +1417,41 @@ NJson::TJsonValue CanonizeJoinOrderImpl(const NJson::TJsonValue& opt) {

auto subplans = opt.GetMapSafe().at("Plans").GetArraySafe();
for (size_t i = 0; i< subplans.size(); ++i) {
res["args"].AppendValue(CanonizeJoinOrderImpl(subplans[i]));
res["args"].AppendValue(GetDetailedJoinOrderImpl(subplans[i]));
}
return res;
}

/* Temporary solution to canonize tests */
NJson::TJsonValue CanonizeJoinOrder(const TString& deserializedPlan) {
NJson::TJsonValue GetDetailedJoinOrder(const TString& deserializedPlan) {
NJson::TJsonValue optRoot;
NJson::ReadJsonTree(deserializedPlan, &optRoot, true);
optRoot = SimplifyPlan(optRoot.GetMapSafe().at("SimplifiedPlan"));
return GetDetailedJoinOrderImpl(SimplifyPlan(optRoot));
}

NJson::TJsonValue GetJoinOrderImpl(const NJson::TJsonValue& opt) {
if (!opt.GetMapSafe().contains("Plans")) {
auto op = opt.GetMapSafe().at("Operators").GetArraySafe()[0];
return op.GetMapSafe().at("Table").GetStringSafe();
}

NJson::TJsonValue res;

auto subplans = opt.GetMapSafe().at("Plans").GetArraySafe();
for (size_t i = 0; i < subplans.size(); ++i) {
res.AppendValue(GetJoinOrderImpl(subplans[i]));
}

return res;
}

NJson::TJsonValue GetJoinOrder(const TString& deserializedPlan) {
NJson::TJsonValue optRoot;
NJson::ReadJsonTree(deserializedPlan, &optRoot, true);
optRoot = SimplifyPlan(optRoot.GetMapSafe().at("SimplifiedPlan"));
return CanonizeJoinOrderImpl(SimplifyPlan(optRoot));
return GetJoinOrderImpl(SimplifyPlan(optRoot));
}


} // namspace NKqp
} // namespace NKikimr
7 changes: 5 additions & 2 deletions ydb/core/kqp/ut/common/kqp_ut_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,11 @@ void WaitForZeroSessions(const NKqp::TKqpCounters& counters);

bool JoinOrderAndAlgosMatch(const TString& optimized, const TString& reference);

/* Temporary solution to canonize tests */
NJson::TJsonValue CanonizeJoinOrder(const TString& deserializedPlan);
/* Gets join order with details as: join algo, join type and scan type. */
NJson::TJsonValue GetDetailedJoinOrder(const TString& deserializedPlan);

/* Gets tables join order without details : only tables. */
NJson::TJsonValue GetJoinOrder(const TString& deserializedPlan);

} // namespace NKqp
} // namespace NKikimr
18 changes: 18 additions & 0 deletions ydb/core/kqp/ut/join/data/queries/join_order_hints_complex.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
PRAGMA TablePathPrefix='/Root';

PRAGMA ydb.OptJoinOrderHints='[ ["R", "S"], ["T", "U"] ]';
PRAGMA ydb.OptCardinalityHints =
'[
{"labels":["R"], "op":"#", "value":10e8},
{"labels":["T"], "op":"#", "value":1},
{"labels":["R", "T"], "op":"#", "value":1},
{"labels":["R", "S"], "op":"#", "value":10e8},
{"labels":["T", "U"], "op":"#", "value":10e8},
{"labels":["V"], "op":"#", "value":1}
]';

SELECT * FROM
R INNER JOIN S on R.id = S.id
INNER JOIN T on R.id = T.id
INNER JOIN U on T.id = U.id
INNER JOIN V on U.id = V.id;
15 changes: 15 additions & 0 deletions ydb/core/kqp/ut/join/data/queries/join_order_hints_simple.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
PRAGMA TablePathPrefix='/Root';

PRAGMA ydb.OptCardinalityHints =
'[
{"labels":["R"], "op":"#", "value":10e8},
{"labels":["T"], "op":"#", "value":1},
{"labels":["S"], "op":"#", "value":10e8},
{"labels":["R", "T"], "op":"#", "value":1},
{"labels":["R", "S"], "op":"#", "value":10e8}
]';
PRAGMA ydb.OptJoinOrderHints='[ "T", ["R", "S"] ]';

SELECT * FROM
R INNER JOIN S on R.id = S.id
INNER JOIN T on R.id = T.id
21 changes: 16 additions & 5 deletions ydb/core/kqp/ut/join/kqp_join_order_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ void ExplainJoinOrderTestDataQuery(const TString& queryPath, bool useStreamLooku
NJson::TJsonValue plan;
NJson::ReadJsonTree(result.GetPlan(), &plan, true);
Cerr << result.GetPlan() << Endl;
Cerr << CanonizeJoinOrder(result.GetPlan()) << Endl;
}
}

Expand All @@ -194,7 +193,7 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
TChainTester(65).Test();
}

void ExecuteJoinOrderTestDataQueryWithStats(const TString& queryPath, const TString& statsPath, bool useStreamLookupJoin, bool useColumnStore) {
TString ExecuteJoinOrderTestDataQueryWithStats(const TString& queryPath, const TString& statsPath, bool useStreamLookupJoin, bool useColumnStore) {
auto kikimr = GetKikimrWithJoinSettings(useStreamLookupJoin, GetStatic(statsPath));
auto db = kikimr.GetTableClient();
auto session = db.CreateSession().GetValueSync().GetSession();
Expand All @@ -208,7 +207,9 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
auto execRes = db.StreamExecuteScanQuery(query, TStreamExecScanQuerySettings().Explain(true)).ExtractValueSync();
execRes.GetIssues().PrintTo(Cerr);
UNIT_ASSERT_VALUES_EQUAL(execRes.GetStatus(), EStatus::SUCCESS);
Cerr << CollectStreamResult(execRes).PlanJson;
auto plan = CollectStreamResult(execRes).PlanJson;
Cerr << plan.GetRef();
return plan.GetRef();
}
}

Expand Down Expand Up @@ -256,7 +257,7 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
);
}

Y_UNIT_TEST_XOR_OR_BOTH_FALSE(FiveWayJoinWithPreds, StreamLookupJoin, ColumnStore) {
Y_UNIT_TEST_XOR_OR_BOTH_FALSE(FiveWayJoinWithPreds, StreamLookupJoin, ColumnStore) {
ExecuteJoinOrderTestDataQueryWithStats(
"queries/five_way_join_with_preds.sql", "stats/basic.json", StreamLookupJoin, ColumnStore
);
Expand Down Expand Up @@ -362,6 +363,16 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
ExecuteJoinOrderTestDataQueryWithStats("queries/tpcds96.sql", "stats/tpcds1000s.json", StreamLookupJoin, ColumnStore);
}

Y_UNIT_TEST_XOR_OR_BOTH_FALSE(TestJoinOrderHintsSimple, StreamLookupJoin, ColumnStore) {
auto plan = ExecuteJoinOrderTestDataQueryWithStats("queries/join_order_hints_simple.sql", "stats/basic.json", StreamLookupJoin, ColumnStore);
UNIT_ASSERT_VALUES_EQUAL(GetJoinOrder(plan).GetStringRobust(), R"(["T",["R","S"]])") ;
}

Y_UNIT_TEST_XOR_OR_BOTH_FALSE(TestJoinOrderHintsComplex, StreamLookupJoin, ColumnStore) {
auto plan = ExecuteJoinOrderTestDataQueryWithStats("queries/join_order_hints_complex.sql", "stats/basic.json", StreamLookupJoin, ColumnStore);
UNIT_ASSERT_VALUES_EQUAL(GetJoinOrder(plan).GetStringRobust(), R"([[["R","S"],["T","U"]],"V"])") ;
}

void JoinOrderTestWithOverridenStats(const TString& queryPath, const TString& statsPath, TString correctJoinOrderPath, bool useStreamLookupJoin, bool useColumnStore
) {
auto kikimr = GetKikimrWithJoinSettings(useStreamLookupJoin, GetStatic(statsPath));
Expand All @@ -386,7 +397,7 @@ Y_UNIT_TEST_SUITE(KqpJoinOrder) {
correctJoinOrderPath = correctJoinOrderPath.substr(0, correctJoinOrderPath.find(".json")) + "_column_store.json";
}

auto currentJoinOrder = CanonizeJoinOrder(result.GetPlan());
auto currentJoinOrder = GetDetailedJoinOrder(result.GetPlan());
Cerr << currentJoinOrder << Endl;
/* to canonize the tests use --test-param CANONIZE_JOIN_ORDER_TESTS=TRUE */
TString canonize = GetTestParam("CANONIZE_JOIN_ORDER_TESTS"); canonize.to_lower();
Expand Down
32 changes: 30 additions & 2 deletions ydb/library/yql/core/cbo/cbo_optimizer_new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
#include <util/generic/hash.h>
#include <util/generic/hash_set.h>
#include <util/string/cast.h>

#include <library/cpp/disjoint_sets/disjoint_sets.h>
#include <util/string/printf.h>

const TString& ToString(NYql::EJoinKind);
const TString& ToString(NYql::EJoinAlgoType);
Expand Down Expand Up @@ -307,6 +306,35 @@ TCardinalityHints::TCardinalityHints(const TString& json) {
}
}

std::shared_ptr<IBaseOptimizerNode> MakeJoinTreeFromJson(const NJson::TJsonValue& jsonTree) {
if (jsonTree.IsArray()) {
auto children = jsonTree.GetArraySafe();
Y_ENSURE(children.size() == 2, Sprintf("Expected 2 inputs for JoinOrder hints, got: %ld", children.size()));

auto joinNode = TJoinOptimizerNode(
MakeJoinTreeFromJson(children[0]),
MakeJoinTreeFromJson(children[1]),
{},
EJoinKind::Cross, // just a stub
EJoinAlgoType::Undefined,
true
);
return std::make_shared<TJoinOptimizerNode>(std::move(joinNode));
}

Y_ENSURE(
jsonTree.IsString(),
Sprintf("A relation must be a string for JoinOrder hints! Got %s, expected a string.", jsonTree.GetStringRobust().c_str())
);
return std::make_shared<TRelOptimizerNode>(jsonTree.GetStringSafe(), nullptr);
}

TJoinOrderHints::TJoinOrderHints(const TString& json) {
NJson::TJsonValue jsonTree;
NJson::ReadJsonTree(json, &jsonTree, true);
HintsTree = MakeJoinTreeFromJson(jsonTree);
}

TJoinAlgoHints::TJoinAlgoHints(const TString& json) {
auto jsonValue = NJson::TJsonValue();
NJson::ReadJsonTree(json, &jsonValue, true);
Expand Down
17 changes: 15 additions & 2 deletions ydb/library/yql/core/cbo/cbo_optimizer_new.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ struct TJoinAlgoHints {
TJoinAlgoHints(const TString& json);
};

struct TJoinOrderHints {
std::shared_ptr<IBaseOptimizerNode> HintsTree;

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

struct TOptimizerHints {
TCardinalityHints CardinalityHints;
TJoinAlgoHints JoinAlgoHints;
TJoinOrderHints JoinOrderHints;
};

/**
* This is a temporary structure for KQP provider
* We will soon be supporting multiple providers and we will need to design
Expand Down Expand Up @@ -235,8 +248,8 @@ struct IOptimizerNew {
virtual ~IOptimizerNew() = default;
virtual std::shared_ptr<TJoinOptimizerNode> JoinSearch(
const std::shared_ptr<TJoinOptimizerNode>& joinTree,
TCardinalityHints hints = {},
TJoinAlgoHints joinHints = {}) = 0;
const TOptimizerHints& hints = {}
) = 0;
};

} // namespace NYql
1 change: 1 addition & 0 deletions ydb/library/yql/dq/opt/bitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ inline bool Overlaps(const TNodeSet& lhs, const TNodeSet& rhs) {
return (lhs & rhs) != 0;
}

/* checks if lhs subset of rhs */
template <typename TNodeSet>
inline bool IsSubset(const TNodeSet& lhs, const TNodeSet& rhs) {
return (lhs & rhs) == lhs;
Expand Down
12 changes: 6 additions & 6 deletions ydb/library/yql/dq/opt/dq_opt_dphyp_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,18 @@ class TDPHypSolver {
TDPHypSolver(
TJoinHypergraph<TNodeSet>& graph,
IProviderContext& ctx,
TCardinalityHints hints,
TJoinAlgoHints joinHints
const TCardinalityHints& hints,
const TJoinAlgoHints& joinHints
)
: Graph_(graph)
, NNodes_(graph.GetNodes().size())
, Pctx_(ctx)
{
for (auto h : hints.Hints) {
for (const auto& h : hints.Hints) {
TNodeSet hintSet = Graph_.GetNodesByRelNames(h.JoinLabels);
CardHintsTable_[hintSet] = h;
}
for (auto h : joinHints.Hints) {
for (const auto& h : joinHints.Hints) {
TNodeSet hintSet = Graph_.GetNodesByRelNames(h.JoinLabels);
JoinAlgoHintsTable_[hintSet] = h;
}
Expand Down Expand Up @@ -485,8 +485,8 @@ template<typename TNodeSet> void TDPHypSolver<TNodeSet>::EmitCsgCmp(const TNodeS

TNodeSet joined = s1 | s2;

auto maybeCardHint = CardHintsTable_.contains(joined) ? & CardHintsTable_.at(joined) : nullptr;
auto maybeJoinAlgoHint = JoinAlgoHintsTable_.contains(joined) ? & JoinAlgoHintsTable_.at(joined) : nullptr;
auto maybeCardHint = CardHintsTable_.contains(joined) ? & CardHintsTable_[joined] : nullptr;
auto maybeJoinAlgoHint = JoinAlgoHintsTable_.contains(joined) ? & JoinAlgoHintsTable_[joined] : nullptr;

auto bestJoin = PickBestJoin(
leftNodes,
Expand Down
Loading