aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaksim Kita <kitaetoya@gmail.com>2023-06-01 14:30:12 +0000
committermaksim-kita <maksim-kita@yandex-team.com>2023-06-01 17:30:12 +0300
commit99a6a4fce57e826d1bc23bdf359bfa4b48121e36 (patch)
treec2ce28700c80d59bcf0d001cc03a7e030fc2e293
parent0fa4c2d7731ee14f0d45501830d5d66377530e26 (diff)
downloadydb-99a6a4fce57e826d1bc23bdf359bfa4b48121e36.tar.gz
"Indexes filter push down over Sort or Limit"
"Indexes filter push down over Sort or Limit" Pull Request resolved: #197
-rw-r--r--ydb/core/kqp/opt/logical/kqp_opt_log.cpp10
-rw-r--r--ydb/core/kqp/opt/logical/kqp_opt_log_indexes.cpp142
-rw-r--r--ydb/core/kqp/opt/logical/kqp_opt_log_rules.h4
-rw-r--r--ydb/core/kqp/opt/physical/kqp_opt_phy_limit.cpp2
-rw-r--r--ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp3
-rw-r--r--ydb/core/kqp/ut/common/kqp_ut_common.cpp5
-rw-r--r--ydb/core/kqp/ut/common/kqp_ut_common.h2
-rw-r--r--ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp71
-rw-r--r--ydb/library/yql/core/yql_opt_utils.cpp9
-rw-r--r--ydb/library/yql/core/yql_opt_utils.h6
10 files changed, 199 insertions, 55 deletions
diff --git a/ydb/core/kqp/opt/logical/kqp_opt_log.cpp b/ydb/core/kqp/opt/logical/kqp_opt_log.cpp
index 41c8dd402e1..6fbd74f0157 100644
--- a/ydb/core/kqp/opt/logical/kqp_opt_log.cpp
+++ b/ydb/core/kqp/opt/logical/kqp_opt_log.cpp
@@ -35,6 +35,7 @@ public:
AddHandler(0, &TDqJoin::Match, HNDL(JoinToIndexLookup));
AddHandler(0, &TCoCalcOverWindowBase::Match, HNDL(ExpandWindowFunctions));
AddHandler(0, &TCoCalcOverWindowGroup::Match, HNDL(ExpandWindowFunctions));
+ AddHandler(0, &TCoFlatMap::Match, HNDL(PushExtractedPredicateToReadTable));
AddHandler(0, &TCoTop::Match, HNDL(RewriteTopSortOverIndexRead));
AddHandler(0, &TCoTopSort::Match, HNDL(RewriteTopSortOverIndexRead));
AddHandler(0, &TCoTake::Match, HNDL(RewriteTakeOverIndexRead));
@@ -49,7 +50,6 @@ public:
AddHandler(0, &TCoTop::Match, HNDL(TopSortOverExtend));
AddHandler(0, &TCoTopSort::Match, HNDL(TopSortOverExtend));
- AddHandler(1, &TCoFlatMap::Match, HNDL(PushExtractedPredicateToReadTable));
AddHandler(1, &TKqlReadTableIndex::Match, HNDL(RewriteIndexRead));
AddHandler(1, &TKqlLookupIndex::Match, HNDL(RewriteLookupIndex));
AddHandler(1, &TKqlStreamLookupIndex::Match, HNDL(RewriteStreamLookupIndex));
@@ -123,14 +123,14 @@ protected:
return output;
}
- TMaybeNode<TExprBase> RewriteTopSortOverIndexRead(TExprBase node, TExprContext& ctx) {
- TExprBase output = KqpRewriteTopSortOverIndexRead(node, ctx, KqpCtx);
+ TMaybeNode<TExprBase> RewriteTopSortOverIndexRead(TExprBase node, TExprContext& ctx, const TGetParents& getParents) {
+ TExprBase output = KqpRewriteTopSortOverIndexRead(node, ctx, KqpCtx, *getParents());
DumpAppliedRule("RewriteTopSortOverIndexRead", node.Ptr(), output.Ptr(), ctx);
return output;
}
- TMaybeNode<TExprBase> RewriteTakeOverIndexRead(TExprBase node, TExprContext& ctx) {
- TExprBase output = KqpRewriteTakeOverIndexRead(node, ctx, KqpCtx);
+ TMaybeNode<TExprBase> RewriteTakeOverIndexRead(TExprBase node, TExprContext& ctx, const TGetParents& getParents) {
+ TExprBase output = KqpRewriteTakeOverIndexRead(node, ctx, KqpCtx, *getParents());
DumpAppliedRule("RewriteTakeOverIndexRead", node.Ptr(), output.Ptr(), ctx);
return output;
}
diff --git a/ydb/core/kqp/opt/logical/kqp_opt_log_indexes.cpp b/ydb/core/kqp/opt/logical/kqp_opt_log_indexes.cpp
index 374898f93d1..19172c04397 100644
--- a/ydb/core/kqp/opt/logical/kqp_opt_log_indexes.cpp
+++ b/ydb/core/kqp/opt/logical/kqp_opt_log_indexes.cpp
@@ -3,6 +3,7 @@
#include <ydb/core/kqp/provider/yql_kikimr_provider_impl.h>
#include <ydb/library/yql/dq/opt/dq_opt_phy.h>
+#include <ydb/library/yql/core/yql_opt_utils.h>
namespace NKikimr::NKqp::NOpt {
@@ -250,19 +251,19 @@ TExprBase DoRewriteIndexRead(const TReadMatch& read, TExprContext& ctx,
TVector<TExprBase> structMembers;
structMembers.reserve(keyColumnsList.Size());
- for (const auto& c : keyColumnsList) {
+ for (const auto& keyColumn : keyColumnsList) {
auto member = Build<TCoNameValueTuple>(ctx, read.Pos())
- .Name().Build(c.Value())
+ .Name().Build(keyColumn.Value())
.Value<TCoMember>()
.Struct(arg)
- .Name().Build(c.Value())
+ .Name().Build(keyColumn.Value())
.Build()
.Done();
structMembers.push_back(member);
}
- readIndexTable= Build<TCoMap>(ctx, read.Pos())
+ readIndexTable = Build<TCoMap>(ctx, read.Pos())
.Input(readIndexTable)
.Lambda()
.Args({arg})
@@ -379,75 +380,138 @@ TExprBase KqpRewriteStreamLookupIndex(const TExprBase& node, TExprContext& ctx,
return node;
}
+/// Can push flat map node to read from table using only columns available in table description
+bool CanPushFlatMap(const TCoFlatMapBase& flatMap, const TKikimrTableDescription& tableDesc, const TParentsMap& parentsMap, TVector<TString> & extraColumns) {
+ auto flatMapLambda = flatMap.Lambda();
+ if (!IsFilterFlatMap(flatMapLambda)) {
+ return false;
+ }
+
+ const auto & flatMapLambdaArgument = flatMapLambda.Args().Arg(0).Ref();
+ auto flatMapLambdaConditional = flatMapLambda.Body().Cast<TCoConditionalValueBase>();
+
+ TSet<TString> lambdaSubset;
+ if (!HaveFieldsSubset(flatMapLambdaConditional.Predicate().Ptr(), flatMapLambdaArgument, lambdaSubset, parentsMap)) {
+ return false;
+ }
+
+ for (auto & lambdaColumn : lambdaSubset) {
+ auto columnIndex = tableDesc.GetKeyColumnIndex(lambdaColumn);
+ if (!columnIndex) {
+ return false;
+ }
+ }
+
+ extraColumns.insert(extraColumns.end(), lambdaSubset.begin(), lambdaSubset.end());
+ return true;
+}
+
// The index and main table have same number of rows, so we can push a copy of TCoTopSort or TCoTake
// through TKqlLookupTable.
// The simplest way is to match TopSort or Take over TKqlReadTableIndex.
-TExprBase KqpRewriteTopSortOverIndexRead(const TExprBase& node, TExprContext& ctx, const TKqpOptimizeContext& kqpCtx) {
+// Additionally if there is TopSort or Take over filter, and filter depends only on columns available in index,
+// we also push copy of filter through TKqlLookupTable.
+TExprBase KqpRewriteTopSortOverIndexRead(const TExprBase& node, TExprContext& ctx, const TKqpOptimizeContext& kqpCtx,
+ const TParentsMap& parentsMap) {
if (!node.Maybe<TCoTopBase>()) {
return node;
}
const auto topBase = node.Maybe<TCoTopBase>().Cast();
- if (auto readTableIndex = TReadMatch::Match(topBase.Input())) {
+ auto maybeFlatMap = topBase.Input().Maybe<TCoFlatMap>();
+ TExprBase input = maybeFlatMap ? maybeFlatMap.Cast().Input() : topBase.Input();
- const auto& tableDesc = GetTableData(*kqpCtx.Tables, kqpCtx.Cluster, readTableIndex.Table().Path());
- const auto& [indexMeta, _ ] = tableDesc.Metadata->GetIndexMetadata(TString(readTableIndex.Index().Value()));
- const auto& indexDesc = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, indexMeta->Name);
+ auto readTableIndex = TReadMatch::Match(input);
+ if (!readTableIndex)
+ return node;
- TVector<TString> sortByColumns;
+ const auto& tableDesc = GetTableData(*kqpCtx.Tables, kqpCtx.Cluster, readTableIndex.Table().Path());
+ const auto& [indexMeta, _ ] = tableDesc.Metadata->GetIndexMetadata(TString(readTableIndex.Index().Value()));
+ const auto& indexDesc = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, indexMeta->Name);
- if (!CanPushTopSort(topBase, indexDesc, &sortByColumns)) {
- return node;
- }
+ TVector<TString> extraColumns;
+
+ if (maybeFlatMap && !CanPushFlatMap(maybeFlatMap.Cast(), indexDesc, parentsMap, extraColumns))
+ return node;
+
+ if (!CanPushTopSort(topBase, indexDesc, &extraColumns)) {
+ return node;
+ }
- auto filter = [&ctx, &node, &topBase](const TExprBase& in) mutable {
- auto newTop = Build<TCoTopBase>(ctx, node.Pos())
- .CallableName(node.Ref().Content())
+ auto filter = [&](const TExprBase& in) mutable {
+ auto sortInput = in;
+
+ if (maybeFlatMap)
+ {
+ sortInput = Build<TCoFlatMap>(ctx, node.Pos())
.Input(in)
- .KeySelectorLambda(ctx.DeepCopyLambda(topBase.KeySelectorLambda().Ref()))
- .SortDirections(topBase.SortDirections())
- .Count(topBase.Count())
+ .Lambda(ctx.DeepCopyLambda(maybeFlatMap.Lambda().Ref()))
.Done();
- return TExprBase(newTop);
- };
-
- auto lookup = DoRewriteIndexRead(readTableIndex, ctx, tableDesc, indexMeta,
- kqpCtx.IsScanQuery(), sortByColumns, filter);
+ }
- return Build<TCoTopBase>(ctx, node.Pos())
+ auto newTop = Build<TCoTopBase>(ctx, node.Pos())
.CallableName(node.Ref().Content())
- .Input(lookup)
+ .Input(sortInput)
.KeySelectorLambda(ctx.DeepCopyLambda(topBase.KeySelectorLambda().Ref()))
.SortDirections(topBase.SortDirections())
.Count(topBase.Count())
.Done();
- }
- return node;
+ return TExprBase(newTop);
+ };
+
+ auto lookup = DoRewriteIndexRead(readTableIndex, ctx, tableDesc, indexMeta,
+ kqpCtx.IsScanQuery(), extraColumns, filter);
+
+ return Build<TCoTopBase>(ctx, node.Pos())
+ .CallableName(node.Ref().Content())
+ .Input(lookup)
+ .KeySelectorLambda(ctx.DeepCopyLambda(topBase.KeySelectorLambda().Ref()))
+ .SortDirections(topBase.SortDirections())
+ .Count(topBase.Count())
+ .Done();
}
-TExprBase KqpRewriteTakeOverIndexRead(const TExprBase& node, TExprContext& ctx, const TKqpOptimizeContext& kqpCtx) {
+TExprBase KqpRewriteTakeOverIndexRead(const TExprBase& node, TExprContext& ctx, const TKqpOptimizeContext& kqpCtx,
+ const TParentsMap& parentsMap) {
if (!node.Maybe<TCoTake>()) {
return node;
}
auto take = node.Maybe<TCoTake>().Cast();
- if (auto readTableIndex = TReadMatch::Match(take.Input())) {
+ auto maybeFlatMap = take.Input().Maybe<TCoFlatMap>();
+ TExprBase input = maybeFlatMap ? maybeFlatMap.Cast().Input() : take.Input();
+
+ auto readTableIndex = TReadMatch::Match(input);
+ if (!readTableIndex)
+ return node;
- const auto& tableDesc = GetTableData(*kqpCtx.Tables, kqpCtx.Cluster, readTableIndex.Table().Path());
- const auto& [indexMeta, _ ] = tableDesc.Metadata->GetIndexMetadata(TString(readTableIndex.Index().Value()));
+ const auto& tableDesc = GetTableData(*kqpCtx.Tables, kqpCtx.Cluster, readTableIndex.Table().Path());
+ const auto& [indexMeta, _ ] = tableDesc.Metadata->GetIndexMetadata(TString(readTableIndex.Index().Value()));
+ const auto& indexDesc = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, indexMeta->Name);
- auto filter = [&ctx, &node](const TExprBase& in) mutable {
- // Change input for TCoTake. New input is result of TKqlReadTable.
- return TExprBase(ctx.ChangeChild(*node.Ptr(), 0, in.Ptr()));
- };
+ TVector<TString> extraColumns;
+ if (maybeFlatMap && !CanPushFlatMap(maybeFlatMap.Cast(), indexDesc, parentsMap, extraColumns))
+ return node;
- return DoRewriteIndexRead(readTableIndex, ctx, tableDesc, indexMeta, kqpCtx.IsScanQuery(), {}, filter);
- }
+ auto filter = [&](const TExprBase& in) mutable {
+ auto takeChild = in;
- return node;
+ if (maybeFlatMap)
+ {
+ takeChild = Build<TCoFlatMap>(ctx, node.Pos())
+ .Input(in)
+ .Lambda(ctx.DeepCopyLambda(maybeFlatMap.Lambda().Ref()))
+ .Done();
+ }
+
+ // Change input for TCoTake. New input is result of TKqlReadTable.
+ return TExprBase(ctx.ChangeChild(*node.Ptr(), 0, takeChild.Ptr()));
+ };
+
+ return DoRewriteIndexRead(readTableIndex, ctx, tableDesc, indexMeta, kqpCtx.IsScanQuery(), extraColumns, filter);
}
} // namespace NKikimr::NKqp::NOpt
diff --git a/ydb/core/kqp/opt/logical/kqp_opt_log_rules.h b/ydb/core/kqp/opt/logical/kqp_opt_log_rules.h
index ecab5b3406a..0a02d10d077 100644
--- a/ydb/core/kqp/opt/logical/kqp_opt_log_rules.h
+++ b/ydb/core/kqp/opt/logical/kqp_opt_log_rules.h
@@ -45,10 +45,10 @@ NYql::NNodes::TExprBase KqpRewriteLookupTable(const NYql::NNodes::TExprBase& nod
const TKqpOptimizeContext& kqpCtx);
NYql::NNodes::TExprBase KqpRewriteTopSortOverIndexRead(const NYql::NNodes::TExprBase& node, NYql::TExprContext&,
- const TKqpOptimizeContext& kqpCtx);
+ const TKqpOptimizeContext& kqpCtx, const NYql::TParentsMap& parentsMap);
NYql::NNodes::TExprBase KqpRewriteTakeOverIndexRead(const NYql::NNodes::TExprBase& node, NYql::TExprContext&,
- const TKqpOptimizeContext& kqpCtx);
+ const TKqpOptimizeContext& kqpCtx, const NYql::TParentsMap& parentsMap);
NYql::NNodes::TExprBase KqpDeleteOverLookup(const NYql::NNodes::TExprBase& node, NYql::TExprContext& ctx,
const TKqpOptimizeContext &kqpCtx);
diff --git a/ydb/core/kqp/opt/physical/kqp_opt_phy_limit.cpp b/ydb/core/kqp/opt/physical/kqp_opt_phy_limit.cpp
index 77f5f426d0c..1509730963b 100644
--- a/ydb/core/kqp/opt/physical/kqp_opt_phy_limit.cpp
+++ b/ydb/core/kqp/opt/physical/kqp_opt_phy_limit.cpp
@@ -18,7 +18,7 @@ TExprBase KqpApplyLimitToReadTable(TExprBase node, TExprContext& ctx, const TKqp
auto input = maybeSkip ? maybeSkip.Cast().Input() : take.Input();
bool isReadTable = input.Maybe<TKqpReadTable>().IsValid();
- bool isReadTableRanges = input.Maybe<TKqpReadTableRanges>().IsValid() || input.Maybe<TKqpReadOlapTableRanges>().IsValid() ;
+ bool isReadTableRanges = input.Maybe<TKqpReadTableRanges>().IsValid() || input.Maybe<TKqpReadOlapTableRanges>().IsValid();
if (!isReadTable && !isReadTableRanges) {
return node;
diff --git a/ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp b/ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp
index 7d39948193f..5b0783775d2 100644
--- a/ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp
+++ b/ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp
@@ -51,9 +51,10 @@ TExprBase KqpRemoveRedundantSortByPk(TExprBase node, TExprContext& ctx, const TK
if (!isReadTable && !isReadTableRanges) {
return node;
}
+
auto& tableDesc = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, GetReadTablePath(input, isReadTableRanges));
- if(tableDesc.Metadata->Kind == EKikimrTableKind::Olap) {
+ if (tableDesc.Metadata->Kind == EKikimrTableKind::Olap) {
// OLAP tables are read in parallel, so we need to keep the out sort.
return node;
}
diff --git a/ydb/core/kqp/ut/common/kqp_ut_common.cpp b/ydb/core/kqp/ut/common/kqp_ut_common.cpp
index cbfaf808998..7243163aed0 100644
--- a/ydb/core/kqp/ut/common/kqp_ut_common.cpp
+++ b/ydb/core/kqp/ut/common/kqp_ut_common.cpp
@@ -919,7 +919,7 @@ std::vector<NJson::TJsonValue> FindPlanStages(const NJson::TJsonValue& plan) {
return stages;
}
-void CreateSampleTablesWithIndex(TSession& session) {
+void CreateSampleTablesWithIndex(TSession& session, bool populateTables) {
auto res = session.ExecuteSchemeQuery(R"(
--!syntax_v1
CREATE TABLE `/Root/SecondaryKeys` (
@@ -950,6 +950,9 @@ void CreateSampleTablesWithIndex(TSession& session) {
)").GetValueSync();
UNIT_ASSERT_C(res.IsSuccess(), res.GetIssues().ToString());
+ if (!populateTables)
+ return;
+
auto result = session.ExecuteDataQuery(R"(
REPLACE INTO `KeyValue` (Key, Value) VALUES
diff --git a/ydb/core/kqp/ut/common/kqp_ut_common.h b/ydb/core/kqp/ut/common/kqp_ut_common.h
index c9bce432095..f24f7bc1412 100644
--- a/ydb/core/kqp/ut/common/kqp_ut_common.h
+++ b/ydb/core/kqp/ut/common/kqp_ut_common.h
@@ -263,7 +263,7 @@ inline void AssertSuccessResult(const NYdb::TStatus& result) {
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
}
-void CreateSampleTablesWithIndex(NYdb::NTable::TSession& session);
+void CreateSampleTablesWithIndex(NYdb::NTable::TSession& session, bool populateTables = true);
// KQP proxy needs to asynchronously receive tenants info before it is able to serve requests that have
// database name specified. Before that it returns errors.
diff --git a/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp b/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp
index 5e9d5ce8df4..5e2554d4648 100644
--- a/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp
+++ b/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp
@@ -3961,6 +3961,77 @@ R"([[#;#;["Primary1"];[41u]];[["Secondary2"];[2u];["Primary2"];[42u]];[["Seconda
CompareYson(R"([[[1];[1];["Payload1"]];[[5];[5];["Payload5"]]])", FormatResultSetYson(result.GetResultSet(0)));
}
+
+ Y_UNIT_TEST(IndexFilterPushDown) {
+ TKikimrRunner kikimr;
+
+ auto db = kikimr.GetTableClient();
+ auto session = db.CreateSession().GetValueSync().GetSession();
+ CreateSampleTablesWithIndex(session, false /*populateTables*/);
+
+ NYdb::NTable::TExecDataQuerySettings execSettings;
+ execSettings.CollectQueryStats(ECollectQueryStatsMode::Basic);
+
+ auto result = session.ExecuteDataQuery(Q1_(R"(
+ REPLACE INTO `/Root/SecondaryKeys` (Key, Fk, Value) VALUES
+ (0, 0, "Value0"),
+ (1, 0, "Value1"),
+ (2, 1, "Value2");
+ )"), TTxControl::BeginTx().CommitTx(), execSettings).ExtractValueSync();
+ UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
+
+ result = session.ExecuteDataQuery(Q1_(R"(
+ SELECT Fk, Value FROM `/Root/SecondaryKeys` VIEW Index WHERE Fk = CAST(0 AS UInt32) LIMIT 1;
+ )"), TTxControl::BeginTx().CommitTx(), execSettings).ExtractValueSync();
+ UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
+
+ AssertTableStats(result, "/Root/SecondaryKeys", {
+ .ExpectedReads = 1
+ });
+
+ AssertTableStats(result, "/Root/SecondaryKeys/Index/indexImplTable", {
+ .ExpectedReads = 1
+ });
+
+ result = session.ExecuteDataQuery(Q1_(R"(
+ SELECT Fk, Value FROM `/Root/SecondaryKeys` VIEW Index WHERE Fk = CAST(0 AS UInt32) AND Fk + Fk >= 0 LIMIT 1;
+ )"), TTxControl::BeginTx().CommitTx(), execSettings).ExtractValueSync();
+ UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
+
+ AssertTableStats(result, "/Root/SecondaryKeys", {
+ .ExpectedReads = 1
+ });
+
+ AssertTableStats(result, "/Root/SecondaryKeys/Index/indexImplTable", {
+ .ExpectedReads = 1
+ });
+
+ result = session.ExecuteDataQuery(Q1_(R"(
+ SELECT Fk, Value FROM `/Root/SecondaryKeys` VIEW Index WHERE Fk = CAST(0 AS UInt32) ORDER BY Fk LIMIT 1;
+ )"), TTxControl::BeginTx().CommitTx(), execSettings).ExtractValueSync();
+ UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
+
+ AssertTableStats(result, "/Root/SecondaryKeys", {
+ .ExpectedReads = 1
+ });
+
+ AssertTableStats(result, "/Root/SecondaryKeys/Index/indexImplTable", {
+ .ExpectedReads = 1
+ });
+
+ result = session.ExecuteDataQuery(Q1_(R"(
+ SELECT Fk, Value FROM `/Root/SecondaryKeys` VIEW Index WHERE Fk = CAST(0 AS UInt32) AND Fk + Fk >= 0 ORDER BY Fk LIMIT 1;
+ )"), TTxControl::BeginTx().CommitTx(), execSettings).ExtractValueSync();
+ UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString());
+
+ AssertTableStats(result, "/Root/SecondaryKeys", {
+ .ExpectedReads = 1
+ });
+
+ AssertTableStats(result, "/Root/SecondaryKeys/Index/indexImplTable", {
+ .ExpectedReads = 1
+ });
+ }
}
}
diff --git a/ydb/library/yql/core/yql_opt_utils.cpp b/ydb/library/yql/core/yql_opt_utils.cpp
index c2e503019c5..83acfe00c5c 100644
--- a/ydb/library/yql/core/yql_opt_utils.cpp
+++ b/ydb/library/yql/core/yql_opt_utils.cpp
@@ -282,9 +282,12 @@ bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TField
return usedFields.size() < inputStructType->GetSize();
}
-template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TSet<TStringBuf>& usedFields, const TParentsMap& parentsMap, bool allowDependsOn);
-template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TSet<TString>& usedFields, const TParentsMap& parentsMap, bool allowDependsOn);
-template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, std::map<std::string_view, TExprNode::TPtr>& usedFields, const TParentsMap& parentsMap, bool allowDependsOn);
+template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TSet<TStringBuf>& usedFields, const TParentsMap& parentsMap,
+ bool allowDependsOn);
+template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TSet<TString>& usedFields, const TParentsMap& parentsMap,
+ bool allowDependsOn);
+template bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, std::map<std::string_view, TExprNode::TPtr>& usedFields,
+ const TParentsMap& parentsMap, bool allowDependsOn);
TExprNode::TPtr AddMembersUsedInside(const TExprNode::TPtr& start, const TExprNode& arg, TExprNode::TPtr&& members, const TParentsMap& parentsMap, TExprContext& ctx) {
if (!members || !start || &arg == start.Get()) {
diff --git a/ydb/library/yql/core/yql_opt_utils.h b/ydb/library/yql/core/yql_opt_utils.h
index 91651a5f7b2..6c0f610777c 100644
--- a/ydb/library/yql/core/yql_opt_utils.h
+++ b/ydb/library/yql/core/yql_opt_utils.h
@@ -27,7 +27,8 @@ TExprNode::TPtr KeepColumnOrder(const TExprNode::TPtr& node, const TExprNode& sr
// returns true if usedFields contains subset of fields
template<class TFieldsSet>
-bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TFieldsSet& usedFields, const TParentsMap& parentsMap, bool allowDependsOn = true);
+bool HaveFieldsSubset(const TExprNode::TPtr& start, const TExprNode& arg, TFieldsSet& usedFields, const TParentsMap& parentsMap,
+ bool allowDependsOn = true);
template<class TFieldsSet>
TExprNode::TPtr FilterByFields(TPositionHandle position, const TExprNode::TPtr& input, const TFieldsSet& subsetFields,
@@ -91,7 +92,8 @@ template <bool Bool>
TExprNode::TPtr MakeBool(TPositionHandle position, TExprContext& ctx);
TExprNode::TPtr MakeIdentityLambda(TPositionHandle position, TExprContext& ctx);
-constexpr std::initializer_list<std::string_view> SkippableCallables = {"Unordered", "AssumeSorted", "AssumeUnique", "AssumeDistinct", "AssumeChopped", "AssumeColumnOrder", "AssumeAllMembersNullableAtOnce"};
+constexpr std::initializer_list<std::string_view> SkippableCallables = {"Unordered", "AssumeSorted", "AssumeUnique", "AssumeDistinct",
+ "AssumeChopped", "AssumeColumnOrder", "AssumeAllMembersNullableAtOnce"};
const TExprNode& SkipCallables(const TExprNode& node, const std::initializer_list<std::string_view>& skipCallables);