diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d15c9c54cf933e04bd2827d8175ff91adee090..64fad3282a1796dda04745378feed7bb5c891754 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ cmake_minimum_required(VERSION 3.13) -set(libcaosdb_VERSION 0.0.9) +set(libcaosdb_VERSION 0.0.10) set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0) set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5) set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0) diff --git a/conanfile.py b/conanfile.py index e2133cea76ce826d168364259d6a519ba28a75d3..0e31f8dfeaae2cd681690324611031bd3017ec67 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools class CaosdbConan(ConanFile): name = "caosdb" - version = "0.0.9" + version = "0.0.10" license = "AGPL-3.0-or-later" author = "Timm C. Fitschen <t.fitschen@indiscale.com>" url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git" diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 48762db4396e8996442ef5be287e3965836ecd4e..e2a2ebc2456de8294ce360434331ed4f710b1e75 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -208,7 +208,7 @@ private: class MultiResultSet : public ResultSet { public: ~MultiResultSet() = default; - explicit MultiResultSet(MultiTransactionResponse *response); + explicit MultiResultSet(std::vector<std::unique_ptr<Entity>> result_set); [[nodiscard]] inline auto Size() const noexcept -> int override { return this->entities.size(); } @@ -357,6 +357,17 @@ public: return *result_set; } + /** + * Return the result of a count query + * + * Only meaningful if there was exactly one COUNT query executed in + * this transaction. In all other cases, the return value will be + * -1. + */ + [[nodiscard]] inline auto GetCountResult() const -> long { + return query_count; + } + /** * Return the number of sub-requests in this transaction. * @@ -367,6 +378,23 @@ public: return this->request->requests_size(); } + /** + * Get a JSON representation of the response. + * + * For debugging. + */ + inline auto ResponseToString() const -> const std::string { + google::protobuf::util::JsonOptions options; + std::string out; + google::protobuf::util::MessageToJsonString(*this->response, &out, options); + return out; + } + + /** + * Get a JSON representation of the request. + * + * For debugging. + */ inline auto RequestToString() const -> const std::string { google::protobuf::util::JsonOptions options; std::string out; @@ -383,6 +411,7 @@ private: MultiTransactionRequest *request; mutable MultiTransactionResponse *response; std::string error_message; + mutable long query_count; }; template <class InputIterator> diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index f946175f3b33397e9398188500cf0ef51cc16d26..61f52786f48594e3182c6101a1be305672e17def 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -96,6 +96,8 @@ using caosdb::entity::v1alpha1::MultiTransactionRequest; using caosdb::entity::v1alpha1::MultiTransactionResponse; using WrappedResponseCase = caosdb::entity::v1alpha1::TransactionResponse::WrappedResponseCase; +using QueryResponseCase = + caosdb::entity::v1alpha1::RetrieveResponse::QueryResponseCase; using caosdb::utility::get_arena; using grpc::ClientAsyncResponseReader; using ProtoEntity = caosdb::entity::v1alpha1::Entity; @@ -131,28 +133,8 @@ auto ResultSet::end() const -> ResultSet::iterator { return ResultSet::iterator(this, Size()); } -MultiResultSet::MultiResultSet(MultiTransactionResponse *response) { - auto *responses = response->mutable_responses(); - for (auto sub_response : *responses) { - switch (sub_response.wrapped_response_case()) { - case WrappedResponseCase::kRetrieveResponse: - this->entities.push_back(std::make_unique<Entity>( - sub_response.mutable_retrieve_response()->release_entity())); - break; - case WrappedResponseCase::kInsertResponse: - this->entities.push_back( - std::make_unique<Entity>(sub_response.release_insert_response())); - break; - case WrappedResponseCase::kDeleteResponse: - this->entities.push_back( - std::make_unique<Entity>(sub_response.release_insert_response())); - break; - default: - // TODO(tf) Updates - break; - } - } -} +MultiResultSet::MultiResultSet(std::vector<std::unique_ptr<Entity>> result_set) + : entities(std::move(result_set)) {} [[nodiscard]] auto UniqueResult::GetEntity() const -> const Entity & { const Entity *result = this->entity.get(); @@ -166,6 +148,7 @@ Transaction::Transaction( response(google::protobuf::Arena::CreateMessage<MultiTransactionResponse>( get_arena())) { this->service_stub = std::move(service_stub); + this->query_count = -1; } auto Transaction::RetrieveById(const std::string &id) noexcept -> StatusCode { @@ -197,7 +180,7 @@ auto Transaction::DeleteById(const std::string &id) noexcept -> StatusCode { auto *sub_request = this->request->add_requests(); sub_request->mutable_delete_request()->set_id(id); - this->status = TransactionStatus::READY(); + this->status = TransactionStatus::GO_ON(); return this->status.GetCode(); } @@ -300,12 +283,29 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus { auto *responses = this->response->mutable_responses(0); switch (responses->wrapped_response_case()) { case WrappedResponseCase::kRetrieveResponse: { - auto *entity = responses->mutable_retrieve_response()->release_entity(); - if (!entity->errors().empty()) { - this->status = TransactionStatus::TRANSACTION_ERROR( - "The request returned with errors."); + auto *retrieve_response = responses->mutable_retrieve_response(); + switch (retrieve_response->query_response_case()) { + case QueryResponseCase::kEntity: { + auto *entity = retrieve_response->release_entity(); + if (!entity->errors().empty()) { + this->status = TransactionStatus::TRANSACTION_ERROR( + "The request returned with errors."); + } + this->result_set = std::make_unique<UniqueResult>(entity); + } break; + case QueryResponseCase::kSelectResult: { + // TODO(tf) Select queries + } break; + case QueryResponseCase::kCountResult: { + this->query_count = retrieve_response->count_result(); + std::vector<std::unique_ptr<Entity>> entities; + this->result_set = + std::make_unique<MultiResultSet>(std::move(entities)); + } break; + default: + // TODO(tf) Error + break; } - this->result_set = std::make_unique<UniqueResult>(entity); } break; case WrappedResponseCase::kUpdateResponse: { auto *updatedIdResponse = responses->mutable_update_response(); @@ -336,7 +336,28 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus { break; } } else { - this->result_set = std::make_unique<MultiResultSet>(this->response); + auto *responses = this->response->mutable_responses(); + std::vector<std::unique_ptr<Entity>> entities; + for (auto sub_response : *responses) { + switch (sub_response.wrapped_response_case()) { + case WrappedResponseCase::kRetrieveResponse: + entities.push_back(std::make_unique<Entity>( + sub_response.mutable_retrieve_response()->release_entity())); + break; + case WrappedResponseCase::kInsertResponse: + entities.push_back( + std::make_unique<Entity>(sub_response.release_insert_response())); + break; + case WrappedResponseCase::kDeleteResponse: + entities.push_back( + std::make_unique<Entity>(sub_response.release_insert_response())); + break; + default: + // TODO(tf) Updates + break; + } + } + this->result_set = std::make_unique<MultiResultSet>(std::move(entities)); } return this->status; diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 557ded7b87fe9947391bb47f6c37b49ee1824cfb..b062cb8a6372ac5db749cc9b6acd998f8829a14a 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -31,6 +31,7 @@ #include "gtest/gtest_pred_impl.h" // for Test, TestInfo, TEST #include <memory> // for allocator, unique_ptr #include <string> // for string, basic_string +#include <utility> // for move #include <vector> // for vector namespace caosdb::transaction { @@ -39,6 +40,7 @@ using caosdb::connection::Connection; using caosdb::exceptions::ConnectionError; using caosdb::transaction::UniqueResult; using ProtoEntity = caosdb::entity::v1alpha1::Entity; +using caosdb::entity::v1alpha1::RetrieveResponse; TEST(test_transaction, create_transaction) { const auto *host = "localhost"; @@ -92,21 +94,18 @@ TEST(test_transaction, test_retrieve_by_ids) { } TEST(test_transaction, test_multi_result_set_empty) { - MultiTransactionResponse response; - - MultiResultSet rs(&response); + std::vector<std::unique_ptr<Entity>> empty; + MultiResultSet rs(std::move(empty)); EXPECT_EQ(rs.Size(), 0); } TEST(test_transaction, test_multi_result_iterator) { - MultiTransactionResponse response; - - response.add_responses() - ->mutable_retrieve_response() - ->mutable_entity() - ->set_id("100"); + std::vector<std::unique_ptr<Entity>> one_elem; + RetrieveResponse response; + response.mutable_entity()->set_id("100"); + one_elem.push_back(std::make_unique<Entity>(response.release_entity())); - MultiResultSet rs(&response); + MultiResultSet rs(std::move(one_elem)); EXPECT_EQ(rs.Size(), 1); for (const Entity &entity : rs) { @@ -126,18 +125,19 @@ TEST(test_transaction, test_unique_result_iterator) { } TEST(test_transaction, test_multi_result_set_one) { - MultiTransactionResponse response; - response.add_responses() - ->mutable_retrieve_response() - ->mutable_entity() - ->set_id("100"); + std::vector<std::unique_ptr<Entity>> one_elem; + RetrieveResponse response; + response.mutable_entity()->set_id("100"); + one_elem.push_back(std::make_unique<Entity>(response.release_entity())); - MultiResultSet rs(&response); + MultiResultSet rs(std::move(one_elem)); EXPECT_EQ(rs.Size(), 1); EXPECT_EQ(rs.At(0).GetId(), "100"); } TEST(test_transaction, test_multi_result_set_three) { + std::vector<std::unique_ptr<Entity>> three_elem; + MultiTransactionResponse response; response.add_responses() ->mutable_retrieve_response() @@ -152,7 +152,14 @@ TEST(test_transaction, test_multi_result_set_three) { ->mutable_entity() ->set_id("102"); - MultiResultSet rs(&response); + auto *responses = response.mutable_responses(); + std::vector<std::unique_ptr<Entity>> entities; + for (auto sub_response : *responses) { + three_elem.push_back(std::make_unique<Entity>( + sub_response.mutable_retrieve_response()->release_entity())); + } + + MultiResultSet rs(std::move(three_elem)); EXPECT_EQ(rs.Size(), 3); EXPECT_TRUE(rs.At(1).HasErrors()); } @@ -170,4 +177,16 @@ TEST(test_transaction, test_update_entity) { EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID); } +TEST(test_transaction, test_multi_deletion) { + const auto *host = "localhost"; + auto configuration = InsecureConnectionConfiguration(host, 8000); + Connection connection(configuration); + auto transaction = connection.CreateTransaction(); + EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::INITIAL); + for (int i = 0; i < 3; i++) { + auto status = transaction->DeleteById("asdf"); + EXPECT_EQ(status, StatusCode::GO_ON); + } +} + } // namespace caosdb::transaction