diff options
author | yplishan <yplishan@ydb.tech> | 2023-11-27 16:36:32 +0300 |
---|---|---|
committer | yplishan <yplishan@ydb.tech> | 2023-11-27 17:58:25 +0300 |
commit | 95e4f8a79178e41d0edff55510b472ba55fcdaba (patch) | |
tree | 68adf81de8db8497aed8b22d9eeec0155167d8df | |
parent | d682298dbe38d7c7a9021001a779c2cd5beaf39f (diff) | |
download | ydb-95e4f8a79178e41d0edff55510b472ba55fcdaba.tar.gz |
Added filtering of database paths in issues in ydb_connector_actor and ydb_schema_query_actor
-rw-r--r-- | ydb/core/fq/libs/common/util.cpp | 39 | ||||
-rw-r--r-- | ydb/core/fq/libs/common/util.h | 5 | ||||
-rw-r--r-- | ydb/core/fq/libs/common/util_ut.cpp | 31 | ||||
-rw-r--r-- | ydb/core/fq/libs/compute/ydb/ydb_connector_actor.cpp | 93 | ||||
-rw-r--r-- | ydb/core/fq/libs/control_plane_proxy/actors/ydb_schema_query_actor.cpp | 3 | ||||
-rw-r--r-- | ydb/tests/fq/s3/test_yq_v2.py | 18 |
6 files changed, 171 insertions, 18 deletions
diff --git a/ydb/core/fq/libs/common/util.cpp b/ydb/core/fq/libs/common/util.cpp index 45f1fdb391..e898ceaa15 100644 --- a/ydb/core/fq/libs/common/util.cpp +++ b/ydb/core/fq/libs/common/util.cpp @@ -1,9 +1,11 @@ #include "util.h" +#include <regex> +#include <re2/re2.h> + #include <util/generic/string.h> #include <util/string/builder.h> #include <util/string/subst.h> -#include <regex> namespace NFq { @@ -41,6 +43,24 @@ EYdbComputeAuth GetBasicAuthMethod(const FederatedQuery::IamAuth& auth) { } } +class TIssueDatabaseRemover { +public: + explicit TIssueDatabaseRemover(const TString& databasePath) + : DatabasePath(databasePath) {} + + TIntrusivePtr<NYql::TIssue> Run(const NYql::TIssue& issue) { + auto msg = RemoveDatabaseFromStr(issue.GetMessage(), DatabasePath); + auto newIssue = MakeIntrusive<NYql::TIssue>(msg); + for (auto issue : issue.GetSubIssues()) { + newIssue->AddSubIssue(Run(*issue)); + } + return newIssue; + } + +private: + TString DatabasePath; +}; + } TString EscapeString(const TString& value, @@ -196,4 +216,21 @@ FederatedQuery::IamAuth GetAuth(const FederatedQuery::Connection& connection) { } } +TString RemoveDatabaseFromStr(TString str, const TString& databasePath) { + TString escapedPath = RE2::QuoteMeta(databasePath); + RE2::GlobalReplace(&str, + TStringBuilder {} << R"(db.\[)" << escapedPath << R"(\/([^ '"]+)\]|)" << escapedPath << R"(\/([^ '"]+))", + R"(\1\2)"); + return str; +} + +NYql::TIssues RemoveDatabaseFromIssues(const NYql::TIssues& issues, const TString& databasePath) { + TIssueDatabaseRemover remover(databasePath); + TVector<NYql::TIssue> newIssues; + for (const auto& issue : issues) { + newIssues.emplace_back(*remover.Run(issue)); + } + return NYql::TIssues(newIssues); +} + } // namespace NFq diff --git a/ydb/core/fq/libs/common/util.h b/ydb/core/fq/libs/common/util.h index 5c6b22e4b0..86664504d5 100644 --- a/ydb/core/fq/libs/common/util.h +++ b/ydb/core/fq/libs/common/util.h @@ -4,6 +4,7 @@ #include <array> #include <google/protobuf/repeated_field.h> +#include <ydb/library/yql/public/issue/yql_issue.h> #include <ydb/public/api/protos/draft/fq.pb.h> #include <library/cpp/iterator/mapped.h> @@ -73,4 +74,8 @@ EYdbComputeAuth GetYdbComputeAuthMethod(const FederatedQuery::ConnectionSetting& FederatedQuery::IamAuth GetAuth(const FederatedQuery::Connection& connection); +TString RemoveDatabaseFromStr(TString str, const TString& substr); + +NYql::TIssues RemoveDatabaseFromIssues(const NYql::TIssues& issues, const TString& str); + } // namespace NFq diff --git a/ydb/core/fq/libs/common/util_ut.cpp b/ydb/core/fq/libs/common/util_ut.cpp index 03862380e4..3e5a7d3e12 100644 --- a/ydb/core/fq/libs/common/util_ut.cpp +++ b/ydb/core/fq/libs/common/util_ut.cpp @@ -34,4 +34,35 @@ Y_UNIT_TEST_SUITE(EscapingBasics) { UNIT_ASSERT_VALUES_EQUAL(EncloseAndEscapeString("some_secret1}+{\n}+{some_secret2", "}+{", "[*]"), "}+{some_secret1[*]\n[*]some_secret2}+{"); } } + +Y_UNIT_TEST_SUITE(IssuesTextFiltering) { + Y_UNIT_TEST(ShouldRemoveDatabasePath) { + TVector<NYql::TIssue> vecIssues; + auto bottomIssue = MakeIntrusive<NYql::TIssue>("Error /path/to/database/binding_name db.[/path/to/database/binding_name]"); + auto midIssue = MakeIntrusive<NYql::TIssue>("'db.[/path/to/database/baz]' /path/to/database/foo /path/to/database/bar"); + midIssue->AddSubIssue(bottomIssue); + NYql::TIssue topIssue("While doing smth db.[] /path/to/other/smth"); + topIssue.AddSubIssue(midIssue); + NYql::TIssues issues({topIssue}); + + TVector<NYql::TIssue> vecIssuesExpected; + auto bottomIssueExpected = MakeIntrusive<NYql::TIssue>("Error binding_name binding_name"); + auto midIssueExpected = MakeIntrusive<NYql::TIssue>("'baz' foo bar"); + midIssueExpected->AddSubIssue(bottomIssueExpected); + NYql::TIssue topIssueExpected("While doing smth db.[] /path/to/other/smth"); + topIssueExpected.AddSubIssue(midIssueExpected); + NYql::TIssues issuesExpected({topIssueExpected}); + + auto issuesActual = RemoveDatabaseFromIssues(issues, "/path/to/database"); + + auto iterActual = issuesActual.begin(); + auto iterExpected = issuesExpected.begin(); + while (iterActual != issuesActual.end()) { + UNIT_ASSERT_VALUES_EQUAL(*iterActual, *iterExpected); + ++iterActual; + ++iterExpected; + } + } +} + } // NFq diff --git a/ydb/core/fq/libs/compute/ydb/ydb_connector_actor.cpp b/ydb/core/fq/libs/compute/ydb/ydb_connector_actor.cpp index b1fb68170a..9a90de8c76 100644 --- a/ydb/core/fq/libs/compute/ydb/ydb_connector_actor.cpp +++ b/ydb/core/fq/libs/compute/ydb/ydb_connector_actor.cpp @@ -1,3 +1,4 @@ +#include <ydb/core/fq/libs/common/util.h> #include <ydb/core/fq/libs/compute/common/run_actor_params.h> #include <ydb/core/fq/libs/compute/ydb/events/events.h> #include <ydb/core/fq/libs/ydb/ydb.h> @@ -58,16 +59,26 @@ public: settings.TraceId(event.TraceId); QueryClient ->ExecuteScript(event.Sql, settings) - .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie](auto future) { + .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie, database = ComputeConnection.database()](auto future) { try { auto response = future.ExtractValueSync(); if (response.Status().IsSuccess()) { actorSystem->Send(recipient, new TEvYdbCompute::TEvExecuteScriptResponse(response.Id(), response.Metadata().ExecutionId), 0, cookie); } else { - actorSystem->Send(recipient, new TEvYdbCompute::TEvExecuteScriptResponse(response.Status().GetIssues(), response.Status().GetStatus()), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvExecuteScriptResponse>( + response.Status().GetIssues(), + response.Status().GetStatus(), database), + 0, cookie); } } catch (...) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvExecuteScriptResponse(NYql::TIssues{NYql::TIssue{CurrentExceptionMessage()}}, NYdb::EStatus::GENERIC_ERROR), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvExecuteScriptResponse>( + CurrentExceptionMessage(), + NYdb::EStatus::GENERIC_ERROR, database), + 0, cookie); } }); } @@ -75,16 +86,26 @@ public: void Handle(const TEvYdbCompute::TEvGetOperationRequest::TPtr& ev) { OperationClient ->Get<NYdb::NQuery::TScriptExecutionOperation>(ev->Get()->OperationId) - .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie](auto future) { + .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie, database = ComputeConnection.database()](auto future) { try { auto response = future.ExtractValueSync(); if (response.Id().GetKind() != Ydb::TOperationId::UNUSED) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvGetOperationResponse(response.Metadata().ExecStatus, static_cast<Ydb::StatusIds::StatusCode>(response.Status().GetStatus()), response.Metadata().ResultSetsMeta, response.Metadata().ExecStats, response.Status().GetIssues()), 0, cookie); + actorSystem->Send(recipient, new TEvYdbCompute::TEvGetOperationResponse(response.Metadata().ExecStatus, static_cast<Ydb::StatusIds::StatusCode>(response.Status().GetStatus()), response.Metadata().ResultSetsMeta, response.Metadata().ExecStats, RemoveDatabaseFromIssues(response.Status().GetIssues(), database)), 0, cookie); } else { - actorSystem->Send(recipient, new TEvYdbCompute::TEvGetOperationResponse(response.Status().GetIssues(), response.Status().GetStatus()), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvGetOperationResponse>( + response.Status().GetIssues(), + response.Status().GetStatus(), database), + 0, cookie); } } catch (...) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvGetOperationResponse(NYql::TIssues{NYql::TIssue{CurrentExceptionMessage()}}, NYdb::EStatus::GENERIC_ERROR), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvGetOperationResponse>( + CurrentExceptionMessage(), + NYdb::EStatus::GENERIC_ERROR, database), + 0, cookie); } }); } @@ -94,16 +115,26 @@ public: settings.FetchToken(ev->Get()->FetchToken); QueryClient ->FetchScriptResults(ev->Get()->OperationId, ev->Get()->ResultSetId, settings) - .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie](auto future) { + .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie, database = ComputeConnection.database()](auto future) { try { auto response = future.ExtractValueSync(); if (response.IsSuccess()) { actorSystem->Send(recipient, new TEvYdbCompute::TEvFetchScriptResultResponse(response.ExtractResultSet(), response.GetNextFetchToken()), 0, cookie); } else { - actorSystem->Send(recipient, new TEvYdbCompute::TEvFetchScriptResultResponse(response.GetIssues(), response.GetStatus()), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvFetchScriptResultResponse>( + response.GetIssues(), + response.GetStatus(), database), + 0, cookie); } } catch (...) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvFetchScriptResultResponse(NYql::TIssues{NYql::TIssue{CurrentExceptionMessage()}}, NYdb::EStatus::GENERIC_ERROR), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvFetchScriptResultResponse>( + CurrentExceptionMessage(), + NYdb::EStatus::GENERIC_ERROR, database), + 0, cookie); } }); } @@ -111,12 +142,22 @@ public: void Handle(const TEvYdbCompute::TEvCancelOperationRequest::TPtr& ev) { OperationClient ->Cancel(ev->Get()->OperationId) - .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie](auto future) { + .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie, database = ComputeConnection.database()](auto future) { try { auto response = future.ExtractValueSync(); - actorSystem->Send(recipient, new TEvYdbCompute::TEvCancelOperationResponse(response.GetIssues(), response.GetStatus()), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvCancelOperationResponse>( + response.GetIssues(), + response.GetStatus(), database), + 0, cookie); } catch (...) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvCancelOperationResponse(NYql::TIssues{NYql::TIssue{CurrentExceptionMessage()}}, NYdb::EStatus::GENERIC_ERROR), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvCancelOperationResponse>( + CurrentExceptionMessage(), + NYdb::EStatus::GENERIC_ERROR, database), + 0, cookie); } }); } @@ -124,15 +165,35 @@ public: void Handle(const TEvYdbCompute::TEvForgetOperationRequest::TPtr& ev) { OperationClient ->Forget(ev->Get()->OperationId) - .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie](auto future) { + .Apply([actorSystem = NActors::TActivationContext::ActorSystem(), recipient = ev->Sender, cookie = ev->Cookie, database = ComputeConnection.database()](auto future) { try { auto response = future.ExtractValueSync(); - actorSystem->Send(recipient, new TEvYdbCompute::TEvForgetOperationResponse(response.GetIssues(), response.GetStatus()), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvForgetOperationResponse>( + response.GetIssues(), + response.GetStatus(), database), + 0, cookie); } catch (...) { - actorSystem->Send(recipient, new TEvYdbCompute::TEvForgetOperationResponse(NYql::TIssues{NYql::TIssue{CurrentExceptionMessage()}}, NYdb::EStatus::GENERIC_ERROR), 0, cookie); + actorSystem->Send( + recipient, + MakeResponse<TEvYdbCompute::TEvForgetOperationResponse>( + CurrentExceptionMessage(), + NYdb::EStatus::GENERIC_ERROR, database), + 0, cookie); } }); } + + template<typename TResponse> + static TResponse* MakeResponse(TString msg, NYdb::EStatus status, TString databasePath) { + return new TResponse(NYql::TIssues{NYql::TIssue{RemoveDatabaseFromStr(msg, databasePath)}}, status); + } + + template<typename TResponse> + static TResponse* MakeResponse(const NYql::TIssues& issues, NYdb::EStatus status, TString databasePath) { + return new TResponse(RemoveDatabaseFromIssues(issues, databasePath), status); + } private: TYqSharedResources::TPtr YqSharedResources; diff --git a/ydb/core/fq/libs/control_plane_proxy/actors/ydb_schema_query_actor.cpp b/ydb/core/fq/libs/control_plane_proxy/actors/ydb_schema_query_actor.cpp index 20fe6a6ca0..fffc41f051 100644 --- a/ydb/core/fq/libs/control_plane_proxy/actors/ydb_schema_query_actor.cpp +++ b/ydb/core/fq/libs/control_plane_proxy/actors/ydb_schema_query_actor.cpp @@ -311,7 +311,8 @@ public: void SaveIssues(const TString& message, const TStatus& status) { auto issue = MakeErrorIssue(TIssuesIds::INTERNAL_ERROR, message); - for (const auto& subIssue : status.GetIssues()) { + auto path = Request->Get()->ComputeDatabase->connection().database(); + for (const auto& subIssue : RemoveDatabaseFromIssues(status.GetIssues(), path)) { issue.AddSubIssue(MakeIntrusive<NYql::TIssue>(subIssue)); } diff --git a/ydb/tests/fq/s3/test_yq_v2.py b/ydb/tests/fq/s3/test_yq_v2.py index 484c801508..88d6837a51 100644 --- a/ydb/tests/fq/s3/test_yq_v2.py +++ b/ydb/tests/fq/s3/test_yq_v2.py @@ -85,3 +85,21 @@ Pear;15;33''' assert result_set.rows[2].items[0].int64_value == 33000000 assert result_set.rows[2].items[1].bytes_value == b"Pear" assert result_set.rows[2].items[2].int32_value == 15 + + @yq_v2 + @pytest.mark.parametrize("client", [{"folder_id": "my_folder"}], indirect=True) + def test_removed_database_path(self, kikimr, s3, client): + kikimr.control_plane.wait_bootstrap(1) + + def validate_query(sql, expected_message): + query_id = client.create_query("simple", sql, type=fq.QueryContent.QueryType.ANALYTICS).result.query_id + response = client.wait_query(query_id, statuses=[fq.QueryMeta.FAILED]) + + actual_message = response.query.issue[0].issues[0].issues[0].issues[0].message + assert expected_message == actual_message + + validate_query(R"SELECT * FROM `non-existing-binding`;", + R"Cannot find table 'non-existing-binding' because it does not exist or you do not have access permissions. Please check correctness of table path and user permissions.") + + validate_query(R"SELECT 1 FROM foo.bar;", + R"Cannot find table 'foo.[bar]' because it does not exist or you do not have access permissions. Please check correctness of table path and user permissions.") |