diff --git a/ydb/core/kqp/opt/physical/kqp_opt_phy_olap_filter.cpp b/ydb/core/kqp/opt/physical/kqp_opt_phy_olap_filter.cpp index 9b43d062b7e2..3f19116102b7 100644 --- a/ydb/core/kqp/opt/physical/kqp_opt_phy_olap_filter.cpp +++ b/ydb/core/kqp/opt/physical/kqp_opt_phy_olap_filter.cpp @@ -807,10 +807,12 @@ bool IsSuitableToCollectProjection(TExprBase node) { // Collects all operations for projections and returns a vector of pair - [columName, olap operation]. TVector> CollectOlapOperationsForProjections(const TExprNode::TPtr& node, const TExprNode& arg, TNodeOnNodeOwnedMap& replaces, - const THashSet& predicateMembers, TExprContext& ctx) { + const THashSet& predicateMembers, + TExprContext& ctx) { auto asStructPred = [](const TExprNode::TPtr& node) -> bool { return !!TMaybeNode(node); }; auto memberPred = [](const TExprNode::TPtr& node) { return !!TMaybeNode(node); }; THashSet projectionMembers; + THashSet notSuitableToPushMembers; ui32 nextMemberId = 0; TVector> olapOperationsForProjections; @@ -818,6 +820,7 @@ TVector> CollectOlapOperationsForProjections if (auto asStruct = FindNode(node, asStructPred)) { // Process each child for `AsStruct` callable. for (auto child : TExprBase(asStruct).Cast()) { + bool memberCollected = false; if (IsSuitableToCollectProjection(child.Item(1))) { // Search for the `TCoMember` in expression, we need expression with only one `TCoMember`. if (auto originalMembers = FindNodes(child.Item(1).Ptr(), memberPred); originalMembers.size() == 1) { @@ -834,17 +837,20 @@ TVector> CollectOlapOperationsForProjections projectionMembers.insert(originalMemberName); } + // clang-format off auto newMember = Build(ctx, node->Pos()) .Struct(originalMember.Struct()) .Name() .Value(originalMemberName) .Build() .Done(); + // clang-format on auto olapOperation = olapOperations.front(); // Replace full expression with only member. replaces[child.Item(1).Raw()] = newMember.Ptr(); olapOperationsForProjections.emplace_back(TString(newMember.Name()), olapOperation.Ptr()); + memberCollected = true; YQL_CLOG(TRACE, ProviderKqp) << "[OLAP PROJECTION] Operation in olap dialect: " << KqpExprToPrettyString(olapOperation, ctx); @@ -852,6 +858,18 @@ TVector> CollectOlapOperationsForProjections } } } + if (!memberCollected) { + auto members = FindNodes(child.Item(1).Ptr(), memberPred); + for (const auto& member : members) { + notSuitableToPushMembers.insert(TString(TExprBase(member).Cast().Name())); + } + } + } + } + + for (const auto& [colName, expr] : olapOperationsForProjections) { + if (notSuitableToPushMembers.count(colName)) { + return {}; } } diff --git a/ydb/core/kqp/ut/olap/kqp_olap_ut.cpp b/ydb/core/kqp/ut/olap/kqp_olap_ut.cpp index fb959a89f3a4..8b55e759420e 100644 --- a/ydb/core/kqp/ut/olap/kqp_olap_ut.cpp +++ b/ydb/core/kqp/ut/olap/kqp_olap_ut.cpp @@ -1861,6 +1861,29 @@ Y_UNIT_TEST_SUITE(KqpOlap) { TString output = FormatResultSetYson(result.GetResultSet(0)); CompareYson(output, results[i]); } + + std::vector notPushedQueries = { + R"( + PRAGMA Kikimr.OptEnableOlapPushdownProjections = "true"; + + SELECT jsonDoc, JSON_VALUE(jsonDoc, "$.\"a.b.c\"") + FROM `/Root/foo` + where b == 1; + )" + }; + + for (ui32 i = 0; i < notPushedQueries.size(); ++i) { + const auto query = notPushedQueries[i]; + auto result = + session2 + .ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx(), NYdb::NQuery::TExecuteQuerySettings().ExecMode(NQuery::EExecMode::Explain)) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + + auto ast = *result.GetStats()->GetAst(); + UNIT_ASSERT_C(ast.find("KqpOlapProjections") == std::string::npos, TStringBuilder() << "Projections pushed down. Query: " << query); + UNIT_ASSERT_C(ast.find("KqpOlapProjection") == std::string::npos, TStringBuilder() << "Projection pushed down. Query: " << query); + } } // Unit tests for datetime pushdowns in query service