diff --git a/.gitignore b/.gitignore index 5ed97f792ec8e3f95437d94e572a2d16c0b39725..7ddf9b7e3c77a0cc492c738c47b5a97a6b7e7997 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,12 @@ flycheck_*.el # Python/Sphinx env/ + +# Conan build files +build_tools +conan.lock +conan_imports_manifest.txt +conanbuildinfo.cmake +conanbuildinfo.txt +conaninfo.txt +graph_info.json diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0ef42ba87dcf92bc82690d837609b5e4a12b67..3bdcb8c0730e90929ce25970850b0c801f1f6d09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +* Integer values are 32 bit now. + ### Deprecated ### Removed ### Fixed +* #11 - Transaction::GetResultSet() now always returns a valid reference. +* #25 - Compiles on MacOS with LLVM now. + ### Security diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6d2aeb16d31fc3282bb391dbbb034d24f3380e..0176a56635fbd890d3e71f8161ba2cc736aa27e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ cmake_minimum_required(VERSION 3.13) -set(libcaosdb_VERSION 0.0.13) +set(libcaosdb_VERSION 0.0.20) set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0) set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5) set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0) @@ -63,6 +63,8 @@ string(REGEX REPLACE "grpc\\+?\\+?_unsecure" "" CONAN_LIBS string(REGEX REPLACE "grpc\\+?\\+?_unsecure" "" CONAN_PKG_LIBS "${CONAN_PKG_LIBS}") +message(STATUS "CONAN_LIBS: ${CONAN_LIBS}") + ########################################### ### GENERAL SETUP of SOURCES ########################################### @@ -415,7 +417,7 @@ option(AUTOFORMATTING "call clang-format at configure time" ON) if(AUTOFORMATTING AND NOT SKIP_LINTING) find_program(clang_format NAMES clang-format-11 clang-format) file(GLOB format_test_sources test/*.cpp test/*.h test/*.h.in) - execute_process(COMMAND $(clang-format) -i --verbose ${libcaosdb_INCL} + execute_process(COMMAND ${clang_format} -i --verbose ${libcaosdb_INCL} ${libcaosdb_SRC} ${libcaosdb_TEST_SRC} ${PROJECT_SOURCE_DIR}/src/cxxcaosdbcli.cpp ${PROJECT_SOURCE_DIR}/src/ccaosdbcli.c diff --git a/Makefile b/Makefile index 6b881b98fc2d90ba4bff31e9d896590222164806..819b9ca2910ce5571f1a80412f655f41afa023e5 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,24 @@ # This Makefile is a wrapper for several other scripts. -CLANG-FORMAT = clang-format-11 +CLANG_FORMAT ?= clang-format-11 +CONAN_SETTINGS = "compiler.libcxx=libstdc++11" + +# OS specific handling, with code by Ken Jackson and oHo, +# from https://stackoverflow.com/a/52062069/232888 and +# https://stackoverflow.com/a/14777895/232888 +ifeq '$(findstring ;,$(PATH))' ';' + DETECTED_OS := Windows +else + DETECTED_OS := $(shell uname 2>/dev/null || echo Unknown) + DETECTED_OS := $(patsubst CYGWIN%,Cygwin,$(DETECTED_OS)) + DETECTED_OS := $(patsubst MSYS%,MSYS,$(DETECTED_OS)) + DETECTED_OS := $(patsubst MINGW%,MSYS,$(DETECTED_OS)) +endif +ifeq ($(DETECTED_OS),Darwin) # Test if we are on MacOS + CONAN_SETTINGS := "compiler.cppstd=17" +endif + .PHONY: help help: @@ -31,10 +48,19 @@ help: @echo " style - auto-format the source files." style: - $(CLANG-FORMAT) -i --verbose \ + $(CLANG_FORMAT) -i --verbose \ $$(find test/ src/ include/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in") .PHONY: style conan-install: - conan create . -s "compiler.libcxx=libstdc++11" -.PHONY: install + conan install . -s $(CONAN_SETTINGS) || \ + (echo "'conan install' failed, trying to build from sources..."; \ + conan install . -s $(CONAN_SETTINGS) --build=missing) +.PHONY: conan-install + +conan-create: + conan create . -s $(CONAN_SETTINGS) +.PHONY: conan-create + +conan: conan-install conan-create +.PHONY: conan diff --git a/conanfile.py b/conanfile.py index 7b34a061a4502c4f303517117ac77f77d622b32b..04d2b1f527b0c5936c0c9a5c7d6d20a98446f1bb 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.13" + version = "0.0.20" license = "AGPL-3.0-or-later" author = "Timm C. Fitschen <t.fitschen@indiscale.com>" url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git" @@ -13,7 +13,11 @@ class CaosdbConan(ConanFile): options = {"shared": [True, False], "fPIC": [True, False]} default_options = {"shared": False, "fPIC": True} generators = "cmake" - requires = [("boost/1.76.0"), ("gtest/1.11.0"), ("grpc/1.38.0")] + requires = [ + ("boost/1.76.0"), + ("gtest/1.11.0"), + ("grpc/1.38.0"), + ] exports = "*.cpp", "*.h", "*.cmake", "*CMakeLists.txt", "*.in", "*.proto", "*.c" exports_sources = "src", "doc", "include", "test", "cmake", "proto" diff --git a/doc/README_SETUP.md b/doc/README_SETUP.md index f73190cad3386518361a9d76dac2f1825d5f3f28..c0b56bfe627596b4765084d1cf4457acfb7e7545 100644 --- a/doc/README_SETUP.md +++ b/doc/README_SETUP.md @@ -27,13 +27,19 @@ command (3.). Instead of the above conan command (2.) use -2. `conan install .. -s "cppstd=11"` +2. `conan install .. -s "compiler.cppstd=17"` and continue as you would when building on a Linux system. You may have to add `build/lib/` (or, alternatively after installation, `CMAKE_INSTALL_PREFIX/lib`) to your `DYLD_LIBRARY_PATH` environmental variable. +#### Problems and solutions #### + +- Make sure that your conan version supports your XCode version. A typical symptom of version + mismatch is for example conan complaining about incompatible `compiler.version` settings. You may + need to rerun any conan and cmake commands (and delete cache files first) after compiler updates. + ### How to build on Windows We use [Visual Studio 2019](https://visualstudio.microsoft.com/de/vs/features/cplusplus/) @@ -50,7 +56,7 @@ as compiler. We use [cmake](https://cmake.org/download/) as build tool. ### Creating a Local Conan Package ## -Building and installing libcaosdb with Conan is just a single command: `make conan-install` +Building and installing libcaosdb with Conan is just a single command: `make conan` For MacOS, you probably should adjust the option as mentioned above. @@ -79,7 +85,9 @@ For the tests there is a slightly different setup required (with option `-D CMAK 3. `cmake -B . -D CMAKE_BUILD_TYPE=Debug ..` * If your clang-format version is too old, formatting, linting etc. can be skipped: `cmake -B . -D CMAKE_BUILD_TYPE=Debug -D SKIP_LINTING=ON ..` -4. `cmake --build .` + * Depending on the clang version it might be necessary to also add + `-DCMAKE_CXX_FLAGS="-Wno-unused-parameter"` +5. `cmake --build .` ### Run diff --git a/doc/capi/index.rst.in b/doc/capi/index.rst.in index a15f3b876035058113a61f2325cb739f01a39fbb..dad5482336472aa01355467687b2441b0a282a7b 100644 --- a/doc/capi/index.rst.in +++ b/doc/capi/index.rst.in @@ -29,7 +29,11 @@ C API When working with libcaosdb's C API keep the following in mind. Delete all objects (transactions, entities, properties, parents, ...) that you created using a `caosdb_..._create_...` - function and only those. + function or released using a `caosdb_..._release_...` and only + those. This means, that any objects set by a `caosdb_..._get_...` + function, are managed by another owning object (e.g., a connection + object managed by the connection manager) should not be deleted + manually. The underlying reason is that all C++ objects are realized in the Extern C interface as mutable structs containing a void pointer to diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index 22358fcda808c8ebf93c140156ba7e39c5757e8d..42d376698872e086c05a5905484e2f63c11dd346 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -40,6 +40,7 @@ using ProtoReferenceDataType = caosdb::entity::v1alpha1::ReferenceDataType; using DataTypeCase = caosdb::entity::v1alpha1::DataType::DataTypeCase; using ListDataTypeCase = caosdb::entity::v1alpha1::ListDataType::ListDataTypeCase; using caosdb::utility::ProtoMessageWrapper; +using caosdb::utility::ScalarProtoMessageWrapper; class Entity; class Property; @@ -68,7 +69,7 @@ const std::map<AtomicDataType, std::string> atomicdatatype_names = { class DataType; class ListDataType; -class ReferenceDataType : public ProtoMessageWrapper<ProtoDataType> { +class ReferenceDataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto GetName() const noexcept -> const std::string & { // is list of reference? @@ -91,11 +92,11 @@ protected: inline static auto Create(ProtoDataType *wrapped) -> std::unique_ptr<ReferenceDataType> { return std::unique_ptr<ReferenceDataType>(new ReferenceDataType(wrapped)); } - ReferenceDataType() : ProtoMessageWrapper<ProtoDataType>() {} - ReferenceDataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} + ReferenceDataType() : ScalarProtoMessageWrapper<ProtoDataType>() {} + ReferenceDataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} }; -class ListDataType : public ProtoMessageWrapper<ProtoDataType> { +class ListDataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto IsListOfReference() const noexcept -> bool { return this->wrapped->list_data_type().list_data_type_case() == @@ -130,21 +131,22 @@ protected: inline static auto Create(ProtoDataType *wrapped) -> std::unique_ptr<ListDataType> { return std::unique_ptr<ListDataType>(new ListDataType(wrapped)); } - ListDataType() : ProtoMessageWrapper<ProtoDataType>() {} + ListDataType() : ScalarProtoMessageWrapper<ProtoDataType>() {} - ListDataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} + ListDataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} mutable std::unique_ptr<ReferenceDataType> reference_data_type; }; -class DataType : public ProtoMessageWrapper<ProtoDataType> { +class DataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: - DataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} - DataType() : ProtoMessageWrapper<ProtoDataType>() {} + DataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} + DataType() : ScalarProtoMessageWrapper<ProtoDataType>(static_cast<ProtoDataType *>(nullptr)) {} /** * Create an AtomicDataType typed DataType. For references, use the std::string constructor. */ - DataType(AtomicDataType data_type, bool list_type = false) : DataType() { + DataType(AtomicDataType data_type, bool list_type = false) + : ScalarProtoMessageWrapper<ProtoDataType>() { if (list_type) { this->wrapped->mutable_list_data_type()->set_atomic_data_type( static_cast<ProtoAtomicDataType>(data_type)); @@ -155,7 +157,8 @@ public: /** * Create a reference typed DataType. */ - DataType(const std::string &data_type, bool list_type = false) : DataType() { + DataType(const std::string &data_type, bool list_type = false) + : ScalarProtoMessageWrapper<ProtoDataType>() { if (list_type) { this->wrapped->mutable_list_data_type()->mutable_reference_data_type()->set_name(data_type); } else { @@ -164,29 +167,30 @@ public: } inline static auto ListOf(const AtomicDataType &atomic_data_type) -> DataType { - DataType result; - result.wrapped->mutable_list_data_type()->set_atomic_data_type( - static_cast<ProtoAtomicDataType>(atomic_data_type)); - return result; + return DataType(atomic_data_type, true); } inline static auto ListOf(const std::string reference_data_type) -> DataType { - DataType result; - result.wrapped->mutable_list_data_type()->mutable_reference_data_type()->set_name( - reference_data_type); - return result; + return DataType(reference_data_type, true); } + [[nodiscard]] inline auto IsUndefined() const noexcept -> bool { + return this->wrapped == nullptr; + } [[nodiscard]] inline auto IsAtomic() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kAtomicDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kAtomicDataType; } - [[nodiscard]] inline auto AsAtomic() const noexcept -> AtomicDataType { + [[nodiscard]] inline auto GetAsAtomic() const noexcept -> AtomicDataType { + if (!IsAtomic()) { + static AtomicDataType undefined; + return undefined; + } return static_cast<AtomicDataType>(this->wrapped->atomic_data_type()); } [[nodiscard]] inline auto IsReference() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kReferenceDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kReferenceDataType; } - [[nodiscard]] inline auto AsReference() const noexcept -> const ReferenceDataType & { + [[nodiscard]] inline auto GetAsReference() const noexcept -> const ReferenceDataType & { if (!IsReference()) { return ReferenceDataType::GetEmptyInstance(); } else if (reference_data_type == nullptr) { @@ -197,10 +201,10 @@ public: } [[nodiscard]] inline auto IsList() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kListDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kListDataType; } - [[nodiscard]] inline auto AsList() const noexcept -> const ListDataType & { + [[nodiscard]] inline auto GetAsList() const noexcept -> const ListDataType & { if (!IsList()) { return ListDataType::GetEmptyInstance(); } else if (list_data_type == nullptr) { @@ -209,9 +213,18 @@ public: return *list_data_type; } + /** + * Return true if `other` is equal to this object. + * + * This compares the underlying wrapped ProtoMessages and return true if they + * are both nullptrs or if the serialization is equal. + */ inline auto operator==(const DataType &other) const noexcept -> bool { - // TODO(tf) Is this safe? - return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + } + // both nullptr? + return this->wrapped == other.wrapped; } friend class Entity; diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index 08a7be6b0f6f2b42752ff78f2a004e7196e9e757..6de939c37f19d9419815dcb8b7001fd19050133f 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -33,6 +33,7 @@ #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField #include "caosdb/logging.h" // for CAOSDB_LOG_WARN #include "caosdb/message_code.h" // for get_message_code +#include "caosdb/protobuf_helper.h" // for get_arena #include "caosdb/status_code.h" // for StatusCode #include "caosdb/value.h" // for Value #include <boost/filesystem/operations.hpp> // for exists, is_di... @@ -44,7 +45,6 @@ #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... #include <cstdint> // for int64_t #include <google/protobuf/message.h> // for RepeatedPtrField -#include <google/protobuf/util/json_util.h> // for MessageToJson... #include <iosfwd> // for streamsize #include <iterator> // for iterator, output_iterato... #include <map> // for map @@ -52,6 +52,7 @@ #include <stdexcept> // for out_of_range #include <string> // for string, basic... #include <vector> // for vector +#include <utility> // for move namespace caosdb::entity { using boost::filesystem::exists; @@ -62,11 +63,15 @@ using ProtoProperty = caosdb::entity::v1alpha1::Property; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using ProtoFileDescriptor = caosdb::entity::v1alpha1::FileDescriptor; using ProtoMessage = caosdb::entity::v1alpha1::Message; +using ProtoValue = caosdb::entity::v1alpha1::Value; +using ProtoDataType = caosdb::entity::v1alpha1::DataType; using caosdb::entity::v1alpha1::EntityRole; using ProtoImportance = caosdb::entity::v1alpha1::Importance; using caosdb::StatusCode; using caosdb::entity::v1alpha1::EntityResponse; using caosdb::entity::v1alpha1::FileTransmissionId; +using caosdb::utility::get_arena; +using caosdb::utility::ProtoMessageWrapper; using ::google::protobuf::RepeatedPtrField; using google::protobuf::RepeatedPtrField; @@ -115,15 +120,18 @@ struct FileDescriptor { * Abstract base class for Messages, Properties and Parents container classes. * * This is a list-like class. + * + * This class wraps a RepeatedPtrField. */ -template <typename T, typename P> class RepeatedPtrFieldWrapper { +template <typename T, typename P> +class RepeatedPtrFieldWrapper : public ProtoMessageWrapper<RepeatedPtrField<P>> { class iterator; public: /** * Return the current size of the container. */ - [[nodiscard]] inline auto size() const -> int { return wrapped->size(); } + [[nodiscard]] inline auto size() const -> int { return this->wrapped->size(); } /** * Return a const reference to the element at the given index. */ @@ -137,10 +145,11 @@ public: throw std::out_of_range("Container has size " + std::to_string(size())); } if (cache.count(index) == 0) { - cache.emplace(index, T(&(wrapped->at(index)))); + cache.emplace(index, T(this->wrapped->Mutable(index))); } return &(cache.at(index)); } + /** * Return iterator positioned at the beginning of the list. */ @@ -162,10 +171,26 @@ public: virtual ~RepeatedPtrFieldWrapper(){}; + inline auto ToString() const noexcept -> const std::string override { + if (this->size() == 0) { + return "[]\n"; + } + std::string result("[\n"); + for (int i = 0; i < this->size();) { + CAOSDB_DEBUG_MESSAGE_STRING(this->wrapped->at(i), next); + result += next; + if (++i < this->size()) { + result.replace(result.size() - 1, 1, ",\n"); + } + } + + return result.append(std::string("]\n")); + } + protected: - RepeatedPtrFieldWrapper(){}; - explicit inline RepeatedPtrFieldWrapper(::google::protobuf::RepeatedPtrField<P> *wrapped) - : wrapped(wrapped){}; + RepeatedPtrFieldWrapper() : ProtoMessageWrapper<RepeatedPtrField<P>>(){}; + explicit inline RepeatedPtrFieldWrapper(RepeatedPtrField<P> *wrapped) + : ProtoMessageWrapper<RepeatedPtrField<P>>(wrapped) {} /** * Append an element. This adds the element to the end of the wrapped list @@ -173,13 +198,7 @@ protected: */ inline auto Append(const T &element) -> void { auto *destination = this->wrapped->Add(); - destination->Swap(element.wrapped); - - // Clear the originally wrapped object and return it to the Arena - element.wrapped->Clear(); - - // set the pointer to the new object which is owned by the RepeatedPtrField - element.wrapped = destination; + destination->CopyFrom(*element.wrapped); } /** @@ -200,9 +219,12 @@ protected: cache.insert(std::move(handle)); } } + // this is possible because cache is an ordered map + cache.erase(cache.find(size()), cache.end()); } - ::google::protobuf::RepeatedPtrField<P> *wrapped; + inline auto Clear() noexcept -> void { this->wrapped->Clear(); } + mutable std::map<int, T> cache; private: @@ -278,7 +300,7 @@ auto RepeatedPtrFieldWrapper<T, P>::end() const -> const RepeatedPtrFieldWrapper * A Message object can be thought of as kinf of a generalized error object in * other frameworks. Please have a look at MessageCodes for more details. */ -class Message { +class Message : public ScalarProtoMessageWrapper<ProtoMessage> { public: /** * Get the code of this message. @@ -306,9 +328,8 @@ public: friend class RepeatedPtrFieldWrapper<Message, ProtoMessage>; private: - explicit inline Message(ProtoMessage *wrapped) : wrapped(wrapped){}; - - ProtoMessage *wrapped; + explicit inline Message(ProtoMessage *wrapped) + : ScalarProtoMessageWrapper<ProtoMessage>(wrapped){}; }; /** @@ -334,10 +355,10 @@ private: * words, this class wraps a protobuf message and provides getter and setter * methods. */ -class Parent { +class Parent : public ScalarProtoMessageWrapper<ProtoParent> { public: - explicit inline Parent(ProtoParent *wrapped) : wrapped(wrapped){}; - Parent(); + explicit inline Parent(ProtoParent *wrapped) : ScalarProtoMessageWrapper<ProtoParent>(wrapped){}; + Parent() : ScalarProtoMessageWrapper<ProtoParent>(){}; /** * Return the id of the parent entity. @@ -361,19 +382,6 @@ public: */ auto SetName(const std::string &name) -> void; - /** - * Return a json string representing this parent. - * - * This is intended for debugging. - */ - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; - } - // TODO(fspreck) Finish the following implementations once we have // decided how to attach messages to parents. // /** @@ -406,22 +414,6 @@ public: friend class RepeatedPtrFieldWrapper<Parent, ProtoParent>; private: - /** - * Return an empty protobuf message pointer. - * - * This function is called by the default constructor of the - * caosdb::entity::Parent class and the protobuf message is used as the - * storage-backend for the new Parent instance. - * - * An 'Arena' takes care of the the memory management. Don't try to delete - * this. - */ - static auto CreateProtoParent() -> ProtoParent *; - - /** - * Message which serves as storage backend. - */ - mutable ProtoParent *wrapped; // Messages errors; // Messages warnings; // Messages infos; @@ -450,14 +442,40 @@ private: * This is a property which belongs to another entity. Don't confuse it with * an Entity with the "Property" role. */ -class Property { +class Property : public ScalarProtoMessageWrapper<ProtoProperty> { public: - explicit inline Property(ProtoProperty *other) - : value(Value(other->mutable_value())), data_type(DataType(other->mutable_data_type())), - wrapped(other) { - FixValue(); + /** + * Copy constructor. + */ + inline Property(const Property &other) + : Property(ProtoMessageWrapper<ProtoProperty>::CopyProtoMessage(other.wrapped)) { + CAOSDB_LOG_TRACE(logger_name) << "Property::Property(const Property &) " + << "- Copy constructor"; }; - Property(); + + /** + * Move constructor. + */ + inline Property(Property &&other) : Property() { + CAOSDB_LOG_TRACE(logger_name) << "Property::Property(Property &&) " + << "- Move constructor"; + this->wrapped = std::move(other.wrapped); + this->value.wrapped = this->wrapped->mutable_value(); + this->data_type.wrapped = this->wrapped->mutable_data_type(); + + other.data_type.wrapped = nullptr; + other.value.wrapped = nullptr; + } + + explicit inline Property(ProtoProperty *other) + : ScalarProtoMessageWrapper<ProtoProperty>(other), + value(this->wrapped->has_value() ? this->wrapped->mutable_value() + : static_cast<ProtoValue *>(nullptr)), + data_type(this->wrapped->has_data_type() ? this->wrapped->mutable_data_type() + : static_cast<ProtoDataType *>(nullptr)){}; + inline Property() + : ScalarProtoMessageWrapper<ProtoProperty>(), value(static_cast<ProtoValue *>(nullptr)), + data_type(static_cast<ProtoDataType *>(nullptr)){}; /** * Return the id of this property @@ -513,15 +531,18 @@ public: * Set the value of this property. */ auto SetValue(const Value &value) -> StatusCode; + auto SetValue(const AbstractValue &value) -> StatusCode; auto SetValue(const std::string &value) -> StatusCode; auto SetValue(const char *value) -> StatusCode; auto SetValue(const double value) -> StatusCode; auto SetValue(const std::vector<std::string> &values) -> StatusCode; auto SetValue(const std::vector<char *> &values) -> StatusCode; auto SetValue(const std::vector<int64_t> &values) -> StatusCode; + auto SetValue(const std::vector<int> &values) -> StatusCode; auto SetValue(const std::vector<double> &values) -> StatusCode; auto SetValue(const std::vector<bool> &values) -> StatusCode; auto SetValue(const int64_t value) -> StatusCode; + auto SetValue(const int value) -> StatusCode; auto SetValue(const bool value) -> StatusCode; /** @@ -536,16 +557,27 @@ public: auto SetDataType(const std::string &new_data_type, bool list_type = false) -> StatusCode; /** - * Return a json string representing this property. - * - * This is intended for debugging - */ - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; + * Copy assignment operator. + */ + auto operator=(const Property &other) -> Property & { + CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(const Property &) " + << "- Copy assignment operator"; + this->wrapped->CopyFrom(*other.wrapped); + this->value = Value(this->wrapped->mutable_value()); + this->data_type = DataType(this->wrapped->mutable_data_type()); + return *this; + } + + /** + * Move assignment operator. + */ + auto operator=(Property &&other) -> Property & { + CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(Property &&) " + << "- Move assignment operator"; + this->wrapped = std::move(other.wrapped); + this->value = std::move(other.value); + this->data_type = std::move(other.data_type); + return *this; } friend class Entity; @@ -553,17 +585,8 @@ public: friend class RepeatedPtrFieldWrapper<Property, ProtoProperty>; private: - /** - * Workaround until non-string values are supported by the server. - * - * Only has an effect if there is a DataType. - */ - auto FixValue() -> void; - static auto CreateProtoProperty() -> ProtoProperty *; Value value; DataType data_type; - - mutable ProtoProperty *wrapped; }; /** @@ -581,71 +604,95 @@ private: * for (auto &property : my_properties) {...} * \endcode */ -class Properties : public RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property> { +class Properties : public RepeatedPtrFieldWrapper<Property, ProtoProperty> { public: ~Properties() = default; friend class Entity; private: inline Properties(){}; - explicit inline Properties( - ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property> *wrapped) - : RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property>(wrapped){}; + explicit inline Properties(RepeatedPtrField<ProtoProperty> *wrapped) + : RepeatedPtrFieldWrapper<Property, ProtoProperty>(wrapped){}; }; /** - * @brief Wrapper for the Protobuf entity. + * Entity is the central and basic data object of CaosDB. + * + * This class is a wrapper of the Entity class auto-generated by protobuf + * (henceforth "ProtoEntity"). + * + * Overview of the Constructors: + * + * <li> Entity() - Calls Entity(ProtoEntity *) with a fresh ProtoEntity + * <li> Entity(Entity) - Copy constructor, calls Entity(ProtoEntity *) after copying wrapped + * ProtoEntity of the original, then also copies all Messages. <li> Entity(ProtoEntity *) - The + * workhorse of the constructors. Initializes everything and does not call other Entity + * constructors. <li> Entity(EntityResponse *) - Constructor which is used by the Transaction class + * to create an Entity from the server's response, calls Entity(ProtoEntity). <li> Entity(IdResponse + * *) - Constructor which is used by the Transaction class to create an Entity from the servers's + * response. calls Entity(), then moves the data to the wrapped ProtoEntity. + * */ -class Entity { +class Entity : public ScalarProtoMessageWrapper<ProtoEntity> { public: - Entity(); inline Entity(const Entity &original) - : wrapped(original.wrapped), value(Value(original.wrapped->mutable_value())), - data_type(DataType(original.wrapped->mutable_data_type())) { - this->wrapped->CopyFrom(*original.wrapped); - data_type.wrapped = this->wrapped->mutable_data_type(); - value.wrapped = this->wrapped->mutable_value(); - properties.wrapped = this->wrapped->mutable_properties(); - parents.wrapped = this->wrapped->mutable_parents(); - errors.wrapped = CreateMessagesField(); - warnings.wrapped = CreateMessagesField(); - infos.wrapped = CreateMessagesField(); - FixValue(); + : Entity(ProtoMessageWrapper::CopyProtoMessage(original.wrapped)) { + this->errors.wrapped->CopyFrom(*original.errors.wrapped); + this->warnings.wrapped->CopyFrom(*original.warnings.wrapped); + this->infos.wrapped->CopyFrom(*original.infos.wrapped); }; - explicit Entity(IdResponse *id_response); explicit Entity(ProtoEntity *other) - : wrapped(other), value(Value(other->mutable_value())), - data_type(DataType(other->mutable_data_type())) { - data_type.wrapped = this->wrapped->mutable_data_type(); - value.wrapped = this->wrapped->mutable_value(); + : ScalarProtoMessageWrapper<ProtoEntity>(other), + value(this->wrapped->has_value() ? this->wrapped->mutable_value() + : static_cast<ProtoValue *>(nullptr)), + data_type(this->wrapped->has_data_type() ? this->wrapped->mutable_data_type() + : static_cast<ProtoDataType *>(nullptr)) { properties.wrapped = this->wrapped->mutable_properties(); parents.wrapped = this->wrapped->mutable_parents(); - errors.wrapped = CreateMessagesField(); - warnings.wrapped = CreateMessagesField(); - infos.wrapped = CreateMessagesField(); - FixValue(); }; explicit inline Entity(EntityResponse *response) : Entity(response->release_entity()) { - errors.wrapped->Swap(response->mutable_errors()); - warnings.wrapped->Swap(response->mutable_warnings()); - infos.wrapped->Swap(response->mutable_infos()); - FixValue(); + this->errors.wrapped->Swap(response->mutable_errors()); + this->warnings.wrapped->Swap(response->mutable_warnings()); + this->infos.wrapped->Swap(response->mutable_infos()); + }; + + explicit inline Entity(IdResponse *id_response) : Entity() { + this->wrapped->set_id(id_response->id()); + this->wrapped->mutable_version()->Swap(id_response->mutable_version()); + this->errors.wrapped->Swap(id_response->mutable_errors()); + this->warnings.wrapped->Swap(id_response->mutable_warnings()); + this->infos.wrapped->Swap(id_response->mutable_infos()); + }; + + explicit inline Entity() + : ScalarProtoMessageWrapper<ProtoEntity>(), value(static_cast<ProtoValue *>(nullptr)), + data_type(static_cast<ProtoDataType *>(nullptr)) { + properties.wrapped = this->wrapped->mutable_properties(); + parents.wrapped = this->wrapped->mutable_parents(); }; - [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { return wrapped->id(); }; - [[nodiscard]] inline auto HasId() const noexcept -> bool { return !wrapped->id().empty(); } + [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { + return this->wrapped->id(); + }; + [[nodiscard]] inline auto HasId() const noexcept -> bool { return !this->wrapped->id().empty(); } [[nodiscard]] inline auto GetVersionId() const -> const std::string & { - return wrapped->version().id(); + return this->wrapped->version().id(); }; - [[nodiscard]] inline auto GetRole() const -> Role { return static_cast<Role>(wrapped->role()); }; - [[nodiscard]] inline auto GetName() const -> const std::string & { return wrapped->name(); }; + [[nodiscard]] inline auto GetRole() const -> Role { + return static_cast<Role>(this->wrapped->role()); + }; + [[nodiscard]] inline auto GetName() const -> const std::string & { + return this->wrapped->name(); + }; [[nodiscard]] inline auto GetDescription() const -> const std::string & { - return wrapped->description(); + return this->wrapped->description(); }; [[nodiscard]] inline auto GetDataType() const -> const DataType & { return this->data_type; }; - [[nodiscard]] inline auto GetUnit() const -> const std::string & { return wrapped->unit(); }; + [[nodiscard]] inline auto GetUnit() const -> const std::string & { + return this->wrapped->unit(); + }; [[nodiscard]] inline auto GetValue() const -> const Value & { return this->value; }; [[nodiscard]] auto GetParents() const -> const Parents &; @@ -659,14 +706,7 @@ public: return this->warnings.wrapped->size() > 0; } [[nodiscard]] auto GetInfos() const -> const Messages & { return infos; } - - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; - } + [[nodiscard]] inline auto HasInfos() const -> bool { return this->infos.wrapped->size() > 0; } auto SetRole(Role role) -> void; auto SetName(const std::string &name) -> void; @@ -675,6 +715,7 @@ public: */ auto SetDescription(const std::string &description) -> void; + auto SetValue(const AbstractValue &value) -> StatusCode; auto SetValue(const Value &value) -> StatusCode; auto SetValue(const std::string &value) -> StatusCode; auto SetValue(const char *value) -> StatusCode; @@ -682,9 +723,11 @@ public: auto SetValue(const std::vector<std::string> &values) -> StatusCode; auto SetValue(const std::vector<char *> &values) -> StatusCode; auto SetValue(const std::vector<int64_t> &values) -> StatusCode; + auto SetValue(const std::vector<int> &values) -> StatusCode; auto SetValue(const std::vector<double> &values) -> StatusCode; auto SetValue(const std::vector<bool> &values) -> StatusCode; auto SetValue(const int64_t value) -> StatusCode; + auto SetValue(const int value) -> StatusCode; auto SetValue(const bool value) -> StatusCode; auto SetUnit(const std::string &unit) -> void; @@ -738,6 +781,12 @@ public: return StatusCode::SUCCESS; } + inline auto ClearMessages() noexcept -> void { + errors.Clear(); + warnings.Clear(); + infos.Clear(); + } + private: inline auto GetNextFileId() -> std::string { std::string str = "0123456789abcdef"; @@ -750,21 +799,12 @@ private: } return result; } - static auto CreateProtoEntity() -> ProtoEntity *; static auto CreateMessagesField() -> RepeatedPtrField<ProtoMessage> *; auto SetId(const std::string &id) -> void; auto SetVersionId(const std::string &id) -> void; - /** - * Workaround until non-string values are supported by the server. - * - * Only has an effect if there is a DataType. - */ - auto FixValue() -> void; - private: FileDescriptor file_descriptor; - ProtoEntity *wrapped; Properties properties; Parents parents; Messages errors; diff --git a/include/caosdb/message_code.h b/include/caosdb/message_code.h index 367b5e9be8c43887e85436ac5491545329de9f10..a277656cd3848931bdc6dff6d3a802dab8da3282 100644 --- a/include/caosdb/message_code.h +++ b/include/caosdb/message_code.h @@ -44,13 +44,22 @@ enum MessageCode { ENTITY_DOES_NOT_EXIST = caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_ENTITY_DOES_NOT_EXIST, ENTITY_HAS_NO_PROPERTIES = caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_ENTITY_HAS_NO_PROPERTIES, + INTEGER_VALUE_OUT_OF_RANGE = + caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_INTEGER_VALUE_OUT_OF_RANGE, + ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY = + caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY, }; [[nodiscard]] inline auto get_message_code(int code) noexcept -> MessageCode { // TODO(tf) smarter, less forgot-it-prone implementation - static MessageCode all_codes[] = {MessageCode::UNSPECIFIED, MessageCode::UNKNOWN, - MessageCode::ENTITY_DOES_NOT_EXIST, - MessageCode::ENTITY_HAS_NO_PROPERTIES}; + static MessageCode all_codes[] = { + MessageCode::UNSPECIFIED, + MessageCode::UNKNOWN, + MessageCode::ENTITY_DOES_NOT_EXIST, + MessageCode::ENTITY_HAS_NO_PROPERTIES, + MessageCode::INTEGER_VALUE_OUT_OF_RANGE, + MessageCode::ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY, + }; for (MessageCode known_code : all_codes) { if (known_code == code) { diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h index 3cfa3d433d540c545fd3d96789e9cec9166022ee..4dfc11c396ded34db6bb2cbed2c938a5402dd7c7 100644 --- a/include/caosdb/protobuf_helper.h +++ b/include/caosdb/protobuf_helper.h @@ -22,14 +22,17 @@ #ifndef CAOSDB_PROTOBUF_HELPER_H #define CAOSDB_PROTOBUF_HELPER_H -#include "caosdb/status_code.h" -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/extension_set.h> // for Arena +#include <google/protobuf/arena.h> // for Arena +// IWYU pragma: no_include "google/protobuf/extension_set.h" +// IWYU pragma: no_include "google/protobuf/generated_message_util.h" +#include <google/protobuf/util/json_util.h> // for JsonOptions, MessageToJs... +#include <string> // for string #define CAOSDB_DEBUG_MESSAGE_STRING(message, out) \ std::string out; \ { \ google::protobuf::util::JsonOptions options; \ + options.add_whitespace = true; \ google::protobuf::util::MessageToJsonString(message, &out, options); \ } @@ -39,19 +42,76 @@ using google::protobuf::Arena; auto get_arena() -> Arena *; +/** + * Abstract wrapper class for Protobuf messages. + */ template <typename P> class ProtoMessageWrapper { public: - ProtoMessageWrapper(const ProtoMessageWrapper &other) = default; - inline auto CopyFrom(const ProtoMessageWrapper &other) noexcept -> StatusCode { - this->wrapped->CopyFrom(*other.wrapped); - return StatusCode::SUCCESS; + virtual ~ProtoMessageWrapper() = 0; + /** + * Return a json representation of this object. + */ + virtual inline auto ToString() const noexcept -> const std::string = 0; + + /** + * Return true if the underlying Protobuf messages have the same + * serialization. + */ + inline auto operator==(const ProtoMessageWrapper<P> &other) const noexcept -> bool { + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + } + // both nullptr? + return this->wrapped == other.wrapped; + } + + /** + * Return true if the underlying Protobuf messages have a different + * serialization. + */ + inline auto operator!=(const ProtoMessageWrapper<P> &other) const noexcept -> bool { + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() != other.wrapped->SerializeAsString(); + } + // only one is nullptr? + return this->wrapped != other.wrapped; } protected: + inline static auto CopyProtoMessage(P *wrapped) -> P * { + P *copy = Arena::CreateMessage<P>(get_arena()); + copy->CopyFrom(*wrapped); + return copy; + } ProtoMessageWrapper() : ProtoMessageWrapper(Arena::CreateMessage<P>(get_arena())) {} ProtoMessageWrapper(P *wrapped) : wrapped(wrapped) {} P *wrapped; }; +template <typename P> ProtoMessageWrapper<P>::~ProtoMessageWrapper() = default; + +/** + * Wrapper class for scalar Protobuf messages. + * + * Scalar means in this context, any message but classes derived from + * RepeatedPtrField. + */ +template <typename P> class ScalarProtoMessageWrapper : public ProtoMessageWrapper<P> { +public: + inline virtual ~ScalarProtoMessageWrapper() = default; + inline ScalarProtoMessageWrapper() = default; + inline ScalarProtoMessageWrapper(P *wrapped) : ProtoMessageWrapper<P>(wrapped) {} + /** + * Return a json representation of this object. + */ + inline auto ToString() const noexcept -> const std::string override { + if (this->wrapped == nullptr) { + return "{}\n"; + } + CAOSDB_DEBUG_MESSAGE_STRING(*this->wrapped, out) + return out; + } +}; + } // namespace caosdb::utility #endif diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h index 6a3b2bda984e9a812e65312f4f338d078c75349d..f6122601f740e54f3f2b9bd72966e6c68de0b279 100644 --- a/include/caosdb/status_code.h +++ b/include/caosdb/status_code.h @@ -22,7 +22,8 @@ #ifndef CAOSDB_STATUS_CODE_H #define CAOSDB_STATUS_CODE_H -#include <string> +#include <grpcpp/impl/codegen/status_code_enum.h> // for StatusCode +#include <string> // for string namespace caosdb { @@ -40,10 +41,23 @@ enum StatusCode { GO_ON = -3, INITIAL = -2, EXECUTING = -1, - SUCCESS = 0, - // TODO(tf) Map other GRPC errors - AUTHENTICATION_ERROR = 16, - CONNECTION_ERROR = 14, + SUCCESS = grpc::StatusCode::OK, // = 0 + CANCELLED = grpc::StatusCode::CANCELLED, // = 1 + UNKNOWN = grpc::StatusCode::UNKNOWN, // = 2 + INVALID_ARGUMENT = grpc::StatusCode::INVALID_ARGUMENT, // = 3 + DEADLINE_EXCEEDED = grpc::StatusCode::DEADLINE_EXCEEDED, // = 4 + NOT_FOUND = grpc::StatusCode::NOT_FOUND, // = 5 + ALREADY_EXISTS = grpc::StatusCode::ALREADY_EXISTS, // = 6 + PERMISSION_DENIED = grpc::StatusCode::PERMISSION_DENIED, // = 7 + RESOURCE_EXHAUSTED = grpc::StatusCode::RESOURCE_EXHAUSTED, // = 8 + FAILED_PRECONDITION = grpc::StatusCode::FAILED_PRECONDITION, // = 9 + ABORTED = grpc::StatusCode::ABORTED, // = 10 + OUT_OF_RANGE = grpc::StatusCode::OUT_OF_RANGE, // = 11 + UNIMPLEMENTED = grpc::StatusCode::UNIMPLEMENTED, // = 12 + INTERNAL = grpc::StatusCode::INTERNAL, // = 13 + CONNECTION_ERROR = grpc::StatusCode::UNAVAILABLE, // = 14 + DATA_LOSS = grpc::StatusCode::DATA_LOSS, // = 15 + AUTHENTICATION_ERROR = grpc::StatusCode::UNAUTHENTICATED, // = 16 GENERIC_RPC_ERROR = 20, GENERIC_ERROR = 21, GENERIC_TRANSACTION_ERROR = 22, @@ -62,7 +76,8 @@ enum StatusCode { FILE_UPLOAD_ERROR = 35, FILE_DOWNLOAD_ERROR = 36, ENUM_MAPPING_ERROR = 37, - FILE_CANNOT_BE_READ = 38, + SPOILED = 38, + FILE_CANNOT_BE_READ = 39, OTHER_CLIENT_ERROR = 9999, }; diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 621c6cbd80057c0f97d171f2ffac91e65b77498f..634c7a0d01b613e4fb6ad4d56e037693d9abc2c2 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -35,9 +35,9 @@ #include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_E... #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_S... #include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... #include <google/protobuf/util/json_util.h> // for MessageToJsonS... #include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <algorithm> // for max #include <iterator> // for iterator, next #include <map> // for map // IWYU pragma: no_include <ext/alloc_traits.h> @@ -92,7 +92,7 @@ } \ switch (this->transaction_type) { \ case NONE: \ - this->transaction_type = TransactionType::DELETE; \ + this->transaction_type = TransactionType::MIXED_WRITE; \ case DELETE: \ case MIXED_WRITE: \ case MIXED_READ_AND_WRITE: \ @@ -114,7 +114,7 @@ } \ switch (this->transaction_type) { \ case NONE: \ - this->transaction_type = TransactionType::INSERT; \ + this->transaction_type = TransactionType::MIXED_WRITE; \ case INSERT: \ case MIXED_WRITE: \ case MIXED_READ_AND_WRITE: \ @@ -136,8 +136,8 @@ } \ switch (this->transaction_type) { \ case NONE: \ - this->transaction_type = TransactionType::INSERT; \ - case INSERT: \ + this->transaction_type = TransactionType::MIXED_WRITE; \ + case UPDATE: \ case MIXED_WRITE: \ case MIXED_READ_AND_WRITE: \ break; \ @@ -195,6 +195,15 @@ public: [[nodiscard]] virtual auto size() const noexcept -> int = 0; [[nodiscard]] virtual auto at(const int index) const -> const Entity & = 0; [[nodiscard]] virtual auto mutable_at(int index) const -> Entity * = 0; + /** + * Return the Entity at the given index. + * + * This method releases the entity from the underlying collection and thus + * leaves the ResultSet in a corrupted state. + * + * This method can be called only once for each index. + */ + [[nodiscard]] virtual auto release_at(int index) -> Entity * = 0; auto begin() const -> iterator; auto end() const -> iterator; @@ -215,6 +224,16 @@ private: class AbstractMultiResultSet : public ResultSet { public: + /** + * Copy Constructor. + * + * Copies the underlying collection of entities. + */ + inline AbstractMultiResultSet(const AbstractMultiResultSet &original) { + for (const Entity &entity : original) { + this->items.push_back(std::make_unique<Entity>(entity)); + } + } virtual ~AbstractMultiResultSet() = default; inline explicit AbstractMultiResultSet(std::vector<std::unique_ptr<Entity>> result_set) : items(std::move(result_set)) {} @@ -225,6 +244,21 @@ public: [[nodiscard]] inline auto mutable_at(int index) const -> Entity * override { return this->items.at(index).get(); } + /** + * Return the Entity at the given index. + * + * This method releases the entity from the underlying collection and thus + * leaves the ResultSet in a corrupted state. + * + * This method can be called only once for each index. + */ + [[nodiscard]] inline auto release_at(int index) -> Entity * override { + return this->items.at(index).release(); + } + /** + * Remove all entities from this result set. + */ + inline auto clear() noexcept -> void { this->items.clear(); } protected: std::vector<std::unique_ptr<Entity>> items; @@ -364,10 +398,43 @@ public: */ [[nodiscard]] inline auto GetStatus() const noexcept -> TransactionStatus { return this->status; } + /** + * Return the ResultSet of this transaction. + * + * Note: If this method is called before the transaction has terminated + * (GetStatus().GetCode() < 0) this method has undefined behavior. + * + * Instead, do Execute() or WaitForIt() and only call this method afterwards. + */ [[nodiscard]] inline auto GetResultSet() const noexcept -> const ResultSet & { + if (!this->result_set) { + CAOSDB_LOG_ERROR(logger_name) + << "GetResultSet was called before the transaction has terminated. This is a programming " + "error of the code which uses the transaction."; + // TODO(tf) This is a really bad SegFault factory. When the transaction + // terminates and the result_set is being overriden, the unique_ptr + // created here will be deleted and any client of the return ResultSet + // will have a SegFault. + this->result_set = std::make_unique<MultiResultSet>(std::vector<std::unique_ptr<Entity>>()); + } return *(this->result_set.get()); } + /** + * Return the ResultSet of this transaction. + * + * This method releases the ResultSet from the transaction leaving it in a + * currupted state. + * + * This method can be called only once and only after the transaction has terminated. + * + * Otherwise, this method has undefined behavior. + */ + [[nodiscard]] inline auto ReleaseResultSet() noexcept -> const ResultSet * { + this->status = TransactionStatus::SPOILED(); + return this->result_set.release(); + } + /** * Return the result of a count query * diff --git a/include/caosdb/transaction_status.h b/include/caosdb/transaction_status.h index 218ac8614ca73ed16d7d15d9793c698a8446c5b9..bdfd65595090acde2b177dd7486492738901b0e0 100644 --- a/include/caosdb/transaction_status.h +++ b/include/caosdb/transaction_status.h @@ -137,6 +137,14 @@ public: */ CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(TRANSACTION_ERROR, StatusCode::GENERIC_TRANSACTION_ERROR) + /** + * Factory for a SPOILED status. + * + * This status means that the transaction's result set has been released and + * GetResultSet() will not return the actual results of the transaction + * anymore. + */ + CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(SPOILED, StatusCode::SPOILED); /** * Another factory for a TRANSACTION_ERROR status with a detailed * description. diff --git a/include/caosdb/utility.h b/include/caosdb/utility.h index 568f8c9bc330356b37ad6c757f85b99d48188776..25ecf6ff12935c2e80391fb9c870769de13e2d3b 100644 --- a/include/caosdb/utility.h +++ b/include/caosdb/utility.h @@ -21,26 +21,25 @@ #ifndef CAOSDB_UTILS_H #define CAOSDB_UTILS_H -#include "caosdb/entity.h" -#include "caosdb/data_type.h" -#include <boost/beast/core/detail/base64.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/filesystem/string_file.hpp> -#include <boost/lexical_cast.hpp> // for lexical_cast -#include <boost/json.hpp> -#include <cassert> -#include <cstdlib> -#include <exception> // for logic_error -#include <fstream> -#include <iostream> -#include <map> -#include "caosdb/exceptions.h" -#include <memory> -#include <mutex> -#include <shared_mutex> -#include <string> -#include <string_view> +#include "caosdb/data_type.h" // for AtomicDataType +#include "caosdb/entity.h" // for Importance, Role +#include <boost/beast/core/detail/base64.hpp> // for encoded_size +#include <boost/beast/core/detail/base64.ipp> // for encode +#include <boost/filesystem/operations.hpp> // for exists +#include <boost/filesystem/path.hpp> // for path +#include <boost/filesystem/fstream.hpp> // for basic_ifstream, ifstream +#include <boost/filesystem/string_file.hpp> // for load_string_file +#include <boost/json/stream_parser.hpp> // for stream_parser +#include <boost/json/value.hpp> // for value +#include <boost/lexical_cast.hpp> // for lexical_cast +#include <cassert> // for assert +#include <cstdlib> // for getenv +#include <fstream> // for basic_istream<>::__ist... +#include <memory> // for allocator, unique_ptr +#include <stdexcept> // for logic_error +#include <string> // for string, operator+, cha... +#include <type_traits> // for underlying_type_t +#include <typeinfo> // for type_info namespace caosdb::utility { using boost::filesystem::exists; @@ -72,9 +71,7 @@ template <> auto getEnumNameFromValue<caosdb::entity::Role>(caosdb::entity::Role * * @detail May be useful for higher-order CaosDB clients and only makes sense if specialized. */ -template <typename Enum> auto getEnumValueFromName(const std::string &name) -> Enum { - throw std::logic_error(std::string("Enum type ") + typeid(Enum).name() + " not implemented."); -} +template <typename Enum> auto getEnumValueFromName(const std::string &name) -> Enum; // Forward declaration of specializations template <> @@ -104,7 +101,7 @@ inline auto load_string_file(const path &path) -> std::string { /** * @brief Return the environment variable KEY, or FALLBACK if it does not exist. */ -inline auto get_env_var(const char *key, const char *fallback) -> const char * { +inline auto get_env_fallback(const char *key, const char *fallback) -> const char * { const char *val = getenv(key); if (val == nullptr) { return fallback; @@ -117,8 +114,9 @@ inline auto get_env_var(const char *key, const char *fallback) -> const char * { * @brief Return the value of an environment variable or - if undefined - the * fallback value. */ -inline auto get_env_var(const std::string &key, const std::string &fallback) -> const std::string { - const char *val = get_env_var(key.c_str(), fallback.c_str()); +inline auto get_env_fallback(const std::string &key, const std::string &fallback) + -> const std::string { + const char *val = get_env_fallback(key.c_str(), fallback.c_str()); auto const result = std::string(val); return result; diff --git a/include/caosdb/value.h b/include/caosdb/value.h index 989f780ed25a535fb34a97281f55e29c005a6c53..8296be311a8262339b58de06d49940100fc183f3 100644 --- a/include/caosdb/value.h +++ b/include/caosdb/value.h @@ -23,29 +23,34 @@ #define CAOSDB_VALUE_H #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message -#include "caosdb/logging.h" -#include <google/protobuf/util/json_util.h> // for MessageToJson... +#include <cstdint> // for int64_t +#include <google/protobuf/arena.h> // for Arena #include <memory> // for unique_ptr -#include <string> // for string +#include <string> // for string, operator== +#include <utility> // for move #include <vector> // for vector #define LIST_VALUE_CONSTRUCTOR(TYPE, SETTER) \ - explicit inline Value(const std::vector<TYPE> &values) : ProtoMessageWrapper<ProtoValue>() { \ + explicit inline Value(const std::vector<TYPE> &values) \ + : ScalarProtoMessageWrapper<ProtoValue>() { \ for (const auto &value : values) { \ this->wrapped->mutable_list_values()->add_values()->SETTER(value); \ } \ } namespace caosdb::entity { +using caosdb::utility::get_arena; using caosdb::utility::ProtoMessageWrapper; +using caosdb::utility::ScalarProtoMessageWrapper; +using google::protobuf::Arena; using ProtoSpecialValue = caosdb::entity::v1alpha1::SpecialValue; using ProtoValue = caosdb::entity::v1alpha1::Value; using ProtoScalarValue = caosdb::entity::v1alpha1::ScalarValue; using ValueCase = caosdb::entity::v1alpha1::Value::ValueCase; using ScalarValueCase = caosdb::entity::v1alpha1::ScalarValue::ScalarValueCase; -class Entity; -class Property; +class ScalarValue; +class Value; // Represents special values which are otherwise hard to tranfer via protobuf. enum SpecialValue { @@ -55,63 +60,257 @@ enum SpecialValue { EMPTY_STRING = ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING, }; -class ScalarValue : public ProtoMessageWrapper<ProtoScalarValue> { +/** + * Pure abstract base class for values. + */ +class AbstractValue { public: - inline ScalarValue(ProtoScalarValue *wrapped) : ProtoMessageWrapper<ProtoScalarValue>(wrapped) {} + /** + * Pure virtual destructor. + */ + virtual ~AbstractValue() = 0; + /** + * Return true iff the value is a NULL value (NULL in the CaosDB sense). + */ + [[nodiscard]] virtual auto IsNull() const noexcept -> bool = 0; + /** + * Return true iff the value is best represented in C++ types as a + * std::string. + * + * If true, clients may call GetAsString to receive a std::string + * representation of the value. + */ + [[nodiscard]] virtual auto IsString() const noexcept -> bool = 0; + /** + * Return true iff the value is best represented in C++ types as a + * bool. + * + * If true, clients may call GetAsBool to receive a bool + * representation of the value. + */ + [[nodiscard]] virtual auto IsBool() const noexcept -> bool = 0; + /** + * Return true iff the value is best represented in C++ types as a + * double. + * + * If true, clients may call GetAsDouble to receive a double + * representation of the value. + */ + [[nodiscard]] virtual auto IsDouble() const noexcept -> bool = 0; + /** + * Return true iff the value is best represented in C++ types as an + * int64_t. + * + * If true, clients may call GetAsInt64 to receive an int64_t + * representation of the value. + */ + [[nodiscard]] virtual auto IsInt64() const noexcept -> bool = 0; + /** + * Return true iff the value is best represented in C++ types as a + * std::vector<ScalarValue>. + * + * If true, clients may call GetAsVector to receive a + * std::vector<ScalarValue> representation of the value. + */ + [[nodiscard]] virtual auto IsVector() const noexcept -> bool = 0; - [[nodiscard]] inline auto IsString() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kStringValue) || + /** + * Return a std::string representation of this value. + * + * Clients should call IsString before calling this function in order to + * assure that this value is indeed best represented by a std::string. + * + * The return value is undefined if IsString is false. + */ + [[nodiscard]] virtual auto GetAsString() const noexcept -> const std::string & = 0; + /** + * Return a bool representation of this value. + * + * Clients should call IsBool before calling this function in order to + * assure that this value is indeed best represented by a bool. + * + * The return value is undefined if IsBool is false. + */ + [[nodiscard]] virtual auto GetAsBool() const noexcept -> bool = 0; + /** + * Return a double representation of this value. + * + * Clients should call IsDouble before calling this function in order to + * assure that this value is indeed best represented by a double. + * + * The return value is undefined if IsDouble is false. + */ + [[nodiscard]] virtual auto GetAsDouble() const noexcept -> double = 0; + /** + * Return an int64_t representation of this value. + * + * Clients should call IsInt64 before calling this function in order to + * assure that this value is indeed best represented by an int64_t. + * + * The return value is undefined if IsInt64 is false. + */ + [[nodiscard]] virtual auto GetAsInt64() const noexcept -> int64_t = 0; + /** + * Return a std::vector<ScalarValue> representation of this value. + * + * Clients should call IsVector before calling this function in order to + * assure that this value is indeed best represented by a + * std::vector<ScalarValue>. + * + * The return value is undefined if IsVector is false. + */ + [[nodiscard]] virtual auto GetAsVector() const noexcept -> const std::vector<ScalarValue> & = 0; + [[nodiscard]] virtual auto ToString() const noexcept -> const std::string = 0; + friend class Value; + +protected: + [[nodiscard]] virtual auto GetProtoValue() const noexcept -> const ProtoValue * = 0; +}; + +inline AbstractValue::~AbstractValue() {} + +class ScalarValue : public AbstractValue, public ScalarProtoMessageWrapper<ProtoScalarValue> { +public: + /** + * Destructor. + */ + inline ~ScalarValue(){}; + /** + * Copy constructor. + */ + inline ScalarValue(const ScalarValue &original) : ScalarValue() { + this->wrapped->CopyFrom(*original.wrapped); + } + /** + * Move constructor. + */ + inline ScalarValue(ScalarValue &&other) : ScalarValue(std::move(other.wrapped)){}; + /** + * Copy assignment operator. + */ + inline auto operator=(const ScalarValue &original) -> ScalarValue & { + if (&original != this) { + this->wrapped->CopyFrom(*original.wrapped); + } + return *this; + } + /** + * Move assignment operator. + */ + inline auto operator=(ScalarValue &&other) -> ScalarValue & { + if (&other != this) { + this->wrapped = std::move(other.wrapped); + } + return *this; + } + + inline ScalarValue(ProtoScalarValue *wrapped) + : ScalarProtoMessageWrapper<ProtoScalarValue>(wrapped), proto_value(nullptr) {} + + [[nodiscard]] inline auto IsNull() const noexcept -> bool { + return this->wrapped == nullptr || (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && - this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING); - return false; + this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED); + } + [[nodiscard]] inline auto IsString() const noexcept -> bool { + return !IsNull() && + ((this->wrapped->scalar_value_case() == ScalarValueCase::kStringValue) || + (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && + this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING)); } - [[nodiscard]] inline auto AsString() const noexcept -> const std::string & { + [[nodiscard]] inline auto GetAsString() const noexcept -> const std::string & { return this->wrapped->string_value(); - ; } [[nodiscard]] inline auto IsDouble() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kDoubleValue); + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kDoubleValue); } - [[nodiscard]] inline auto AsDouble() const noexcept -> double { + [[nodiscard]] inline auto GetAsDouble() const noexcept -> double { return this->wrapped->double_value(); } - [[nodiscard]] inline auto IsInteger() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kIntegerValue); + [[nodiscard]] inline auto IsInt64() const noexcept -> bool { + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kIntegerValue); } - [[nodiscard]] inline auto AsInteger() const noexcept -> int64_t { + [[nodiscard]] inline auto GetAsInt64() const noexcept -> int64_t { return this->wrapped->integer_value(); } [[nodiscard]] inline auto IsBool() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kBooleanValue); + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kBooleanValue); } - [[nodiscard]] inline auto AsBool() const noexcept -> bool { + [[nodiscard]] inline auto GetAsBool() const noexcept -> bool { return this->wrapped->boolean_value(); } + [[nodiscard]] auto IsVector() const noexcept -> bool { + // Always false b/c a scalar value is never a collection. + return false; + } + [[nodiscard]] auto GetAsVector() const noexcept -> const std::vector<ScalarValue> & { + // Always return an empty vector. + static const std::vector<ScalarValue> empty_collection; + return empty_collection; + } + [[nodiscard]] inline auto ToString() const noexcept -> const std::string { + return ScalarProtoMessageWrapper::ToString(); + } + + friend class Value; + +protected: + [[nodiscard]] auto GetProtoValue() const noexcept -> const ProtoValue * { + if (this->proto_value == nullptr) { + this->proto_value = Arena::CreateMessage<ProtoValue>(get_arena()); + this->proto_value->mutable_scalar_value()->CopyFrom(*this->wrapped); + } + return this->proto_value; + }; + inline ScalarValue() : ScalarProtoMessageWrapper<ProtoScalarValue>(), proto_value(nullptr) {} + +private: + mutable ProtoValue *proto_value; }; -class Value : public ProtoMessageWrapper<ProtoValue> { +class Value : public AbstractValue, public ScalarProtoMessageWrapper<ProtoValue> { public: - inline Value() : ProtoMessageWrapper<ProtoValue>() { + /** + * Copy constructor. + */ + inline Value(const Value &original) : ScalarProtoMessageWrapper<ProtoValue>() { + this->wrapped->CopyFrom(*original.wrapped); + } + /** + * Move constructor. + */ + inline Value(Value &&other) : Value(std::move(other.wrapped)) {} + /** + * Destructor. + */ + inline ~Value() {} + inline Value() : ScalarProtoMessageWrapper<ProtoValue>(static_cast<ProtoValue *>(nullptr)) { // has NULL_VALUE now } - explicit inline Value(ProtoValue *wrapped) : ProtoMessageWrapper<ProtoValue>(wrapped) {} - explicit inline Value(const std::string &value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(const ScalarValue &value) : ScalarProtoMessageWrapper<ProtoValue>() { + this->wrapped->mutable_scalar_value()->CopyFrom(*value.wrapped); + } + explicit inline Value(const AbstractValue &value) : ScalarProtoMessageWrapper<ProtoValue>() { + this->wrapped->CopyFrom(*value.GetProtoValue()); + } + explicit inline Value(ProtoValue *wrapped) : ScalarProtoMessageWrapper<ProtoValue>(wrapped) {} + explicit inline Value(const std::string &value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_string_value(value); } - explicit inline Value(const char *value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(const char *value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_string_value(std::string(value)); } - explicit inline Value(double value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(double value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_double_value(value); } - explicit inline Value(int64_t value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(int64_t value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_integer_value(value); } - explicit inline Value(int value) : Value((int64_t)value) {} - explicit inline Value(bool value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(int value) : Value(static_cast<int64_t>(value)) {} + explicit inline Value(bool value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_boolean_value(value); } @@ -123,11 +322,15 @@ public: LIST_VALUE_CONSTRUCTOR(bool, set_boolean_value) [[nodiscard]] inline auto IsNull() const noexcept -> bool { - return this->wrapped->value_case() == ValueCase::VALUE_NOT_SET; + return this->wrapped == nullptr || + (this->wrapped->value_case() == ValueCase::VALUE_NOT_SET || + (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kSpecialValue && + this->wrapped->scalar_value().special_value() == + ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED)); } [[nodiscard]] inline auto IsString() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kStringValue) || (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kSpecialValue && @@ -136,76 +339,105 @@ public: } return false; } - [[nodiscard]] inline auto AsString() const noexcept -> const std::string & { + [[nodiscard]] inline auto GetAsString() const noexcept -> const std::string & { + if (!IsString()) { + static std::string empty_string; + return empty_string; + } return this->wrapped->scalar_value().string_value(); - ; } [[nodiscard]] inline auto IsDouble() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kDoubleValue); } return false; } - [[nodiscard]] inline auto AsDouble() const noexcept -> double { + [[nodiscard]] inline auto GetAsDouble() const noexcept -> double { + if (!IsDouble()) + return 0.0; return this->wrapped->scalar_value().double_value(); } - [[nodiscard]] inline auto IsInteger() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + [[nodiscard]] inline auto IsInt64() const noexcept -> bool { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kIntegerValue); } return false; } - [[nodiscard]] inline auto AsInteger() const noexcept -> int64_t { + [[nodiscard]] inline auto GetAsInt64() const noexcept -> int64_t { + if (!IsInt64()) + return 0; return this->wrapped->scalar_value().integer_value(); } [[nodiscard]] inline auto IsBool() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kBooleanValue); } return false; } - [[nodiscard]] inline auto AsBool() const noexcept -> bool { + [[nodiscard]] inline auto GetAsBool() const noexcept -> bool { + if (!IsBool()) + return false; return this->wrapped->scalar_value().boolean_value(); } - [[nodiscard]] inline auto IsList() const noexcept -> bool { - return this->wrapped->value_case() == ValueCase::kListValues; + [[nodiscard]] inline auto IsVector() const noexcept -> bool { + return !IsNull() && this->wrapped->value_case() == ValueCase::kListValues; } - [[nodiscard]] inline auto AsList() const noexcept -> const std::vector<ScalarValue> & { - if (!IsList()) { + [[nodiscard]] inline auto GetAsVector() const noexcept -> const std::vector<ScalarValue> & { + if (!IsVector()) { // create empty list - this->list_values = std::make_unique<std::vector<ScalarValue>>(); + static std::vector<ScalarValue> empty_values; + return empty_values; } - if (this->list_values == nullptr) { - this->list_values = std::make_unique<std::vector<ScalarValue>>(); + if (this->collection_values == nullptr) { + this->collection_values = std::make_unique<std::vector<ScalarValue>>(); for (auto &scalar : *(this->wrapped->mutable_list_values()->mutable_values())) { - this->list_values->push_back(ScalarValue(&scalar)); + this->collection_values->push_back(ScalarValue(&scalar)); } } - return *(this->list_values); + return *(this->collection_values); } - inline auto operator==(const Value &other) const noexcept -> bool { - return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + /** + * Copy assignment operator. + */ + inline auto operator=(const Value &other) -> Value & { + if (&other != this) { + this->wrapped->CopyFrom(*other.wrapped); + this->collection_values.reset(); + } + return *this; } - inline auto ToString() const noexcept -> const std::string { - CAOSDB_DEBUG_MESSAGE_STRING(*wrapped, out) - CAOSDB_LOG_DEBUG("caosdb::entity") << "HERE 1 [" << wrapped << "] " << out; - return out; + /** + * Move assignment operator. + */ + inline auto operator=(Value &&other) -> Value & { + if (&other != this) { + this->wrapped = std::move(other.wrapped); + this->collection_values.reset(); + } + return *this; + } + + [[nodiscard]] inline auto ToString() const noexcept -> const std::string { + return ScalarProtoMessageWrapper::ToString(); } friend class Entity; friend class Property; +protected: + [[nodiscard]] auto GetProtoValue() const noexcept -> const ProtoValue * { return this->wrapped; }; + private: - mutable std::unique_ptr<std::vector<ScalarValue>> list_values; + mutable std::unique_ptr<std::vector<ScalarValue>> collection_values; }; } // namespace caosdb::entity diff --git a/include/ccaosdb.h b/include/ccaosdb.h index b06f3ec388d14a2b15b362babd99e3207a7353ef..4d1b99dd151fbf09a53d9b80c0963ee3d3368c67 100644 --- a/include/ccaosdb.h +++ b/include/ccaosdb.h @@ -19,6 +19,9 @@ * <https://www.gnu.org/licenses/>. * */ + +#include <cstdint> // for int64_t + #ifdef __cplusplus extern "C" { #endif @@ -65,7 +68,7 @@ int caosdb_status_code_OTHER_CLIENT_ERROR(); * capability for type checking in C even though the C++ class * Connection is opaque in C. */ -typedef struct { +typedef struct caosdb_connection_connection { void *wrapped_connection; bool _deletable = false; } caosdb_connection_connection; @@ -77,7 +80,7 @@ typedef struct { * capability for type checking in C even though the C++ class * Connection is opaque in C. */ -typedef struct { +typedef struct caosdb_connection_connection_configuration { void *wrapped_connection_configuration; bool _deletable = false; } caosdb_connection_connection_configuration; @@ -89,7 +92,7 @@ typedef struct { * capability for type checking in C even though the C++ class * Connection is opaque in C. */ -typedef struct { +typedef struct caosdb_info_version_info { int major; int minor; int patch; @@ -97,12 +100,12 @@ typedef struct { const char *build; } caosdb_info_version_info; -typedef struct { +typedef struct caosdb_connection_certificate_provider { void *wrapped_certificate_provider; bool _deletable = false; } caosdb_connection_certificate_provider; -typedef struct { +typedef struct caosdb_authentication_authenticator { void *wrapped_authenticator; bool _deletable = false; } caosdb_authentication_authenticator; @@ -112,7 +115,7 @@ typedef struct { * * If the environment variable is not set, return the fallback instead. */ -const char *caosdb_utility_get_env_var(const char *name, const char *fallback); +const char *caosdb_utility_get_env_fallback(const char *name, const char *fallback); /** * Return a description of the status code. @@ -261,9 +264,7 @@ int caosdb_connection_connection_manager_get_connection(caosdb_connection_connec * ENTITY STUFF AND TRANSACTIONS ****************************************************************************/ -// TODO(fspreck) implementations needed, and probably these declarations are -// not sufficient yet. -typedef struct { +typedef struct caosdb_transaction_transaction { void *wrapped_transaction; bool _deletable = false; } caosdb_transaction_transaction; @@ -289,22 +290,51 @@ int caosdb_transaction_transaction_execute(caosdb_transaction_transaction *trans // TODO(fspreck) execute_asynchronously may be added as a separate // function once we actually support asynchronous execution. -typedef struct { +typedef struct caosdb_transaction_result_set { void *wrapped_result_set; bool _deletable = false; } caosdb_transaction_result_set; +typedef struct caosdb_entity_entity { + void *wrapped_entity; + bool _deletable = false; +} caosdb_entity_entity; + int caosdb_transaction_transaction_get_result_set(caosdb_transaction_transaction *transaction, caosdb_transaction_result_set *out); +/** + * Release the result set from the transaction. + * + * The transactions is spoiled after this action and should not be used anymore. + * + * Note: The result_set has to be deleted via caosdb_transaction_delete_result_set. + * + * EXPERT USE ONLY. Only use it when you know what you are doing. + */ +int caosdb_transaction_transaction_release_result_set(caosdb_transaction_transaction *transaction, + caosdb_transaction_result_set *out); +/** + * Release the entity from the result set. + * + * Each entity (each index) can be released once. The result set is spoiled + * after this action and should not be used for anything else anymore. + * + * Note: The result_set has to be deleted via caosdb_entity_delete_entity. + * + * EXPERT USE ONLY. Only use it when you know what you are doing. + */ +int caosdb_transaction_result_set_release_at(caosdb_transaction_result_set *result_set, + caosdb_entity_entity *entity, int index); +/** + * Destructor for caosdb_transaction_result_set. + * + * EXPERT USE ONLY. Only use it when you know what you are doing. + */ +int caosdb_transaction_delete_result_set(caosdb_transaction_result_set *result_set); int caosdb_transaction_transaction_get_count_result(caosdb_transaction_transaction *transaction, long *out); -typedef struct { - void *wrapped_entity; - bool _deletable = false; -} caosdb_entity_entity; - int caosdb_transaction_result_set_at(caosdb_transaction_result_set *result_set, caosdb_entity_entity *entity, int index); int caosdb_transaction_result_set_size(caosdb_transaction_result_set *result_set, int *out); @@ -316,46 +346,40 @@ int caosdb_transaction_transaction_update_entity(caosdb_transaction_transaction int caosdb_transaction_transaction_delete_by_id(caosdb_transaction_transaction *transaction, const char *id); -typedef struct { +typedef struct caosdb_entity_property { void *wrapped_property; bool _deletable = false; } caosdb_entity_property; -typedef struct { + +typedef struct caosdb_entity_parent { void *wrapped_parent; bool _deletable = false; } caosdb_entity_parent; -typedef struct { + +typedef struct caosdb_entity_message { void *wrapped_message; bool _deletable = false; } caosdb_entity_message; +typedef struct caosdb_entity_value { + void *wrapped_value; + bool _deletable = false; +} caosdb_entity_value; + +typedef struct caosdb_entity_datatype { + void *wrapped_datatype; + bool _deletable = false; +} caosdb_entity_datatype; + // GETTERS FOR EVERYTHING int caosdb_entity_entity_get_id(caosdb_entity_entity *entity, char **out); int caosdb_entity_entity_get_role(caosdb_entity_entity *entity, char **out); int caosdb_entity_entity_get_name(caosdb_entity_entity *entity, char **out); int caosdb_entity_entity_get_description(caosdb_entity_entity *entity, char **out); int caosdb_entity_entity_get_local_path(caosdb_entity_entity *entity, char **out); -/** - * Get the name of the entity's datatype, whether it is a reference, and whether it is a list. - */ -int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, char **name, bool *is_ref, - bool *is_list); +int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, caosdb_entity_datatype *out); int caosdb_entity_entity_get_unit(caosdb_entity_entity *entity, char **out); - -int caosdb_entity_entity_get_int_value(caosdb_entity_entity *entity, long *out); -int caosdb_entity_entity_get_double_value(caosdb_entity_entity *entity, double *out); -int caosdb_entity_entity_get_boolean_value(caosdb_entity_entity *entity, bool *out); -int caosdb_entity_entity_get_string_value(caosdb_entity_entity *entity, char **out); -int caosdb_entity_entity_get_int_list_value_at(caosdb_entity_entity *entity, long *out, - const int index); -int caosdb_entity_entity_get_double_list_value_at(caosdb_entity_entity *entity, double *out, - const int index); -int caosdb_entity_entity_get_boolean_list_value_at(caosdb_entity_entity *entity, bool *out, - const int index); -int caosdb_entity_entity_get_string_list_value_at(caosdb_entity_entity *entity, char **out, - const int index); -int caosdb_entity_entity_get_value_list_length(caosdb_entity_entity *entity, int *out); - +int caosdb_entity_entity_get_value(caosdb_entity_entity *entity, caosdb_entity_value *out); int caosdb_entity_entity_get_version_id(caosdb_entity_entity *entity, char **out); int caosdb_entity_entity_get_errors_size(caosdb_entity_entity *entity, int *out); int caosdb_entity_entity_get_error(caosdb_entity_entity *entity, caosdb_entity_message *out, @@ -377,27 +401,10 @@ int caosdb_entity_property_get_id(caosdb_entity_property *property, char **out); int caosdb_entity_property_get_name(caosdb_entity_property *property, char **out); int caosdb_entity_property_get_description(caosdb_entity_property *property, char **out); int caosdb_entity_property_get_importance(caosdb_entity_property *property, char **out); -/** - * Get the name of the property's datatype, whether it is a reference, and whether it is a list. - */ -int caosdb_entity_property_get_datatype(caosdb_entity_property *property, char **name, bool *is_ref, - bool *is_list); +int caosdb_entity_property_get_datatype(caosdb_entity_property *property, + caosdb_entity_datatype *out); int caosdb_entity_property_get_unit(caosdb_entity_property *property, char **out); - -int caosdb_entity_property_get_int_value(caosdb_entity_property *property, long *out); -int caosdb_entity_property_get_double_value(caosdb_entity_property *property, double *out); -int caosdb_entity_property_get_boolean_value(caosdb_entity_property *property, bool *out); -int caosdb_entity_property_get_string_value(caosdb_entity_property *property, char **out); -int caosdb_entity_property_get_int_list_value_at(caosdb_entity_property *property, long *out, - const int index); -int caosdb_entity_property_get_double_list_value_at(caosdb_entity_property *property, double *out, - const int index); -int caosdb_entity_property_get_boolean_list_value_at(caosdb_entity_property *property, bool *out, - const int index); -int caosdb_entity_property_get_string_list_value_at(caosdb_entity_property *property, char **out, - const int index); -int caosdb_entity_property_get_value_list_length(caosdb_entity_property *property, int *out); - +int caosdb_entity_property_get_value(caosdb_entity_property *property, caosdb_entity_value *out); int caosdb_entity_parent_get_id(caosdb_entity_parent *parent, char **out); int caosdb_entity_parent_get_name(caosdb_entity_parent *parent, char **out); int caosdb_entity_parent_get_description(caosdb_entity_parent *parent, char **out); @@ -405,6 +412,27 @@ int caosdb_entity_parent_get_description(caosdb_entity_parent *parent, char **ou int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out); int caosdb_entity_message_get_description(caosdb_entity_message *message, char **out); +int caosdb_entity_datatype_is_undefined(caosdb_entity_datatype *datatype, bool *out); +int caosdb_entity_datatype_is_atomic(caosdb_entity_datatype *datatype, bool *out); +int caosdb_entity_datatype_is_reference(caosdb_entity_datatype *datatype, bool *out); +int caosdb_entity_datatype_is_list_of_atomic(caosdb_entity_datatype *datatype, bool *out); +int caosdb_entity_datatype_is_list_of_reference(caosdb_entity_datatype *datatype, bool *out); +int caosdb_entity_datatype_get_datatype_name(caosdb_entity_datatype *datatype, char **out); + +int caosdb_entity_value_is_null(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_is_string(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_is_double(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_is_integer(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_is_bool(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_is_vector(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_get_as_string(caosdb_entity_value *value, char **out); +int caosdb_entity_value_get_as_double(caosdb_entity_value *value, double *out); +int caosdb_entity_value_get_as_integer(caosdb_entity_value *value, int64_t *out); +int caosdb_entity_value_get_as_bool(caosdb_entity_value *value, bool *out); +int caosdb_entity_value_get_as_vector_size(caosdb_entity_value *value, int *out); +int caosdb_entity_value_get_as_vector_at(caosdb_entity_value *value, caosdb_entity_value *out, + const int index); + // CONSTRUCTORS AND DESTRUCTORS int caosdb_entity_create_entity(caosdb_entity_entity *out); int caosdb_entity_delete_entity(caosdb_entity_entity *out); @@ -413,31 +441,38 @@ int caosdb_entity_delete_property(caosdb_entity_property *out); int caosdb_entity_create_parent(caosdb_entity_parent *out); int caosdb_entity_delete_parent(caosdb_entity_parent *out); +// DATATYPE CONSTRUCTORS for atomic and reference datatypes and lists thereof +int caosdb_entity_create_atomic_datatype(caosdb_entity_datatype *out, const char *name); +int caosdb_entity_create_reference_datatype(caosdb_entity_datatype *out, const char *name); +int caosdb_entity_create_atomic_list_datatype(caosdb_entity_datatype *out, const char *name); +int caosdb_entity_create_reference_list_datatype(caosdb_entity_datatype *out, const char *name); +int caosdb_entity_delete_datatype(caosdb_entity_datatype *out); + +// VALUE CONSTRUCTORS (resolve overloaded constructors) +int caosdb_entity_create_int_value(caosdb_entity_value *out, const int64_t value); +int caosdb_entity_create_string_value(caosdb_entity_value *out, const char *value); +int caosdb_entity_create_double_value(caosdb_entity_value *out, const double value); +int caosdb_entity_create_bool_value(caosdb_entity_value *out, const bool value); +int caosdb_entity_create_int_vector_value(caosdb_entity_value *out, const int64_t *value, + const int length); +int caosdb_entity_create_string_vector_value(caosdb_entity_value *out, const char **value, + const int length); +int caosdb_entity_create_double_vector_value(caosdb_entity_value *out, const double *value, + const int length); +int caosdb_entity_create_bool_vector_value(caosdb_entity_value *out, const bool *value, + const int length); +int caosdb_entity_delete_value(caosdb_entity_value *out); + // SETTERS FOR EVERYTHING THAT MAY BE SET int caosdb_entity_entity_set_role(caosdb_entity_entity *entity, const char *role); int caosdb_entity_entity_set_name(caosdb_entity_entity *entity, const char *name); int caosdb_entity_entity_set_description(caosdb_entity_entity *entity, const char *description); int caosdb_entity_entity_set_local_path(caosdb_entity_entity *entity, const char *name); int caosdb_entity_entity_set_file_path(caosdb_entity_entity *entity, const char *name); -/** - * Set the entity's datatype by name, and whether it is a reference or a list. - */ -int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity, const char *datatype, - const bool is_ref, const bool is_list); +int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity, + caosdb_entity_datatype *datatype); int caosdb_entity_entity_set_unit(caosdb_entity_entity *entity, const char *unit); -// TODO(fspreck) replace by more specific setters -int caosdb_entity_entity_set_int_value(caosdb_entity_entity *entity, const long value); -int caosdb_entity_entity_set_double_value(caosdb_entity_entity *entity, const double value); -int caosdb_entity_entity_set_boolean_value(caosdb_entity_entity *entity, const bool value); -int caosdb_entity_entity_set_string_value(caosdb_entity_entity *entity, const char *value); -int caosdb_entity_entity_set_int_list_value(caosdb_entity_entity *entity, const long *value, - const int length); -int caosdb_entity_entity_set_double_list_value(caosdb_entity_entity *entity, const double *value, - const int length); -int caosdb_entity_entity_set_boolean_list_value(caosdb_entity_entity *entity, const bool *value, - const int length); -int caosdb_entity_entity_set_string_list_value(caosdb_entity_entity *entity, const char **value, - const int length); +int caosdb_entity_entity_set_value(caosdb_entity_entity *entity, caosdb_entity_value *value); int caosdb_entity_entity_append_parent(caosdb_entity_entity *entity, caosdb_entity_parent *parent); int caosdb_entity_entity_remove_parent(caosdb_entity_entity *entity, int index); @@ -447,26 +482,11 @@ int caosdb_entity_entity_remove_property(caosdb_entity_entity *entity, int index int caosdb_entity_property_set_id(caosdb_entity_property *property, const char *id); int caosdb_entity_property_set_name(caosdb_entity_property *property, const char *name); -/** - * Set the property's datatype by name, and whether it is a reference or a list. - */ -int caosdb_entity_property_set_datatype(caosdb_entity_property *property, const char *datatype, - const bool is_ref, const bool is_list); +int caosdb_entity_property_set_datatype(caosdb_entity_property *property, + caosdb_entity_datatype *datatype); int caosdb_entity_property_set_importance(caosdb_entity_property *property, const char *importance); int caosdb_entity_property_set_unit(caosdb_entity_property *property, const char *unit); - -int caosdb_entity_property_set_int_value(caosdb_entity_property *property, const long value); -int caosdb_entity_property_set_double_value(caosdb_entity_property *property, const double value); -int caosdb_entity_property_set_boolean_value(caosdb_entity_property *property, const bool value); -int caosdb_entity_property_set_string_value(caosdb_entity_property *property, const char *value); -int caosdb_entity_property_set_int_list_value(caosdb_entity_property *property, const long *value, - const int length); -int caosdb_entity_property_set_double_list_value(caosdb_entity_property *property, - const double *value, const int length); -int caosdb_entity_property_set_boolean_list_value(caosdb_entity_property *property, - const bool *value, const int length); -int caosdb_entity_property_set_string_list_value(caosdb_entity_property *property, - const char **value, const int length); +int caosdb_entity_property_set_value(caosdb_entity_property *property, caosdb_entity_value *value); int caosdb_entity_parent_set_id(caosdb_entity_parent *parent, const char *id); int caosdb_entity_parent_set_name(caosdb_entity_parent *parent, const char *name); diff --git a/proto b/proto index 6f81c44a02b9258293bfb83b4de7831ef8d7c4e9..73d85fb20bb16902c0a89dda697eed17994712bc 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 6f81c44a02b9258293bfb83b4de7831ef8d7c4e9 +Subproject commit 73d85fb20bb16902c0a89dda697eed17994712bc diff --git a/requirements.txt b/requirements.txt index 2911ae69101c3c4bd492308915b7a5a653a2d170..4bf95a29fc3c9d28931a25664c2456f22041be3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ bottle==0.12.19 certifi==2021.5.30 chardet==4.0.0 colorama==0.4.4 -conan==1.37.2 +conan==1.40.3 deprecation==2.0.7 distro==1.5.0 fasteners==0.16.3 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb00898b70776b99c197556c435398d254578cbf..a3654e01f5aa6f184338ff74070755df87f9147c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ set(libcaosdb_SRC ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/download_request_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_writer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_reader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/status_code_description.cpp ) # pass variable to parent scope diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index 017aac5ca6ab28d6773c16ca7dc9662e5990c2d4..bb73431e4af00ed1db48b8f87fa883db6f4f9b31 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -22,16 +22,12 @@ #include "caosdb/entity.h" #include "caosdb/data_type.h" // for DataType #include "caosdb/entity/v1alpha1/main.pb.h" // for Messages -#include "caosdb/exceptions.h" -#include "caosdb/protobuf_helper.h" // for get_arena -#include "caosdb/value.h" // for Value -#include <boost/algorithm/string.hpp> -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for Arena::Create... -#include <new> // for operator new +#include "caosdb/protobuf_helper.h" // for get_arena +#include "caosdb/value.h" // for Value +#include <google/protobuf/arena.h> // for Arena +#include <new> // for operator new namespace caosdb::entity { -using caosdb::entity::v1alpha1::IdResponse; using ProtoParent = caosdb::entity::v1alpha1::Parent; using ProtoProperty = caosdb::entity::v1alpha1::Property; using ProtoEntity = caosdb::entity::v1alpha1::Entity; @@ -44,24 +40,8 @@ using google::protobuf::Arena; Messages::~Messages() = default; -// Forward declarations /////////////////////////////////////////////////////// - -template <typename E> auto FixValueImpl(E *ent) -> void; - // Parent ///////////////////////////////////////////////////////////////////// -Parent::Parent() : wrapped(Parent::CreateProtoParent()) { - // TODO(fspreck) Re-enable once we have decided how to attach - // messages to parents. - // errors.wrapped = this->wrapped->mutable_errors(); - // warnings.wrapped = this->wrapped->mutable_warnings(); - // infos.wrapped = this->wrapped->mutable_infos(); -} - -auto Parent::CreateProtoParent() -> ProtoParent * { - return Arena::CreateMessage<ProtoParent>(get_arena()); -} - auto Parent::SetName(const std::string &name) -> void { this->wrapped->set_name(name); } auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } @@ -74,11 +54,7 @@ auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } return this->wrapped->description(); } -Property::Property() : Property(Property::CreateProtoProperty()) { FixValue(); } - -auto Property::CreateProtoProperty() -> ProtoProperty * { - return Arena::CreateMessage<ProtoProperty>(get_arena()); -} +// Property /////////////////////////////////////////////////////////////////// [[nodiscard]] auto Property::GetId() const -> const std::string & { return this->wrapped->id(); } @@ -114,7 +90,18 @@ auto Property::SetImportance(Importance importance) -> void { this->wrapped->set_importance(static_cast<ProtoImportance>(importance)); } -auto Property::SetValue(const Value &value) -> StatusCode { return this->value.CopyFrom(value); } +auto Property::SetValue(const Value &value) -> StatusCode { + if (value.wrapped == nullptr) { + this->wrapped->clear_value(); + this->value = Value(); + } else { + this->wrapped->mutable_value()->CopyFrom(*value.wrapped); + this->value = Value(this->wrapped->mutable_value()); + } + return StatusCode::SUCCESS; +} + +auto Property::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); } auto Property::SetValue(const std::string &value) -> StatusCode { return SetValue(Value(value)); } @@ -134,6 +121,10 @@ auto Property::SetValue(const std::vector<int64_t> &values) -> StatusCode { return SetValue(Value(values)); } +auto Property::SetValue(const std::vector<int> &values) -> StatusCode { + return SetValue(Value(values)); +} + auto Property::SetValue(const std::vector<double> &values) -> StatusCode { return SetValue(Value(values)); } @@ -144,12 +135,21 @@ auto Property::SetValue(const std::vector<bool> &values) -> StatusCode { auto Property::SetValue(const int64_t value) -> StatusCode { return SetValue(Value(value)); } +auto Property::SetValue(const int value) -> StatusCode { return SetValue(Value(value)); } + auto Property::SetValue(const bool value) -> StatusCode { return SetValue(Value(value)); } auto Property::SetUnit(const std::string &unit) -> void { this->wrapped->set_unit(unit); } auto Property::SetDataType(const DataType &new_data_type) -> StatusCode { - return this->data_type.CopyFrom(new_data_type); + if (new_data_type.wrapped == nullptr) { + this->wrapped->clear_data_type(); + this->data_type = DataType(); + } else { + this->wrapped->mutable_data_type()->CopyFrom(*new_data_type.wrapped); + this->data_type = DataType(this->wrapped->mutable_data_type()); + } + return StatusCode::SUCCESS; } auto Property::SetDataType(const AtomicDataType new_data_type, bool list_type) -> StatusCode { @@ -160,8 +160,6 @@ auto Property::SetDataType(const std::string &new_data_type, bool list_type) -> return SetDataType(DataType(new_data_type, list_type)); } -auto Property::FixValue() -> void { FixValueImpl(this); } - // Entity ///////////////////////////////////////////////////////////////////// [[nodiscard]] auto Entity::GetParents() const -> const Parents & { return parents; } @@ -175,21 +173,6 @@ auto Entity::AppendProperty(const Property &property) -> void { this->properties auto Entity::RemoveProperty(int index) -> void { this->properties.Remove(index); } -auto Entity::CreateProtoEntity() -> ProtoEntity * { - return Arena::CreateMessage<ProtoEntity>(get_arena()); -} - -Entity::Entity(IdResponse *id_response) : Entity() { - this->wrapped->set_id(id_response->id()); - this->wrapped->mutable_version()->Swap(id_response->mutable_version()); - this->errors.wrapped->Swap(id_response->mutable_errors()); - this->warnings.wrapped->Swap(id_response->mutable_warnings()); - this->infos.wrapped->Swap(id_response->mutable_infos()); - FixValue(); -} - -Entity::Entity() : Entity(Entity::CreateProtoEntity()) { FixValue(); } - auto Entity::CreateMessagesField() -> RepeatedPtrField<ProtoMessage> * { return Arena::CreateMessage<RepeatedPtrField<ProtoMessage>>(get_arena()); } @@ -214,9 +197,18 @@ auto Entity::SetValue(const Value &value) -> StatusCode { if (GetRole() != Role::PROPERTY) { return StatusCode::ENTITY_CANNOT_HAVE_A_VALUE; } - return this->value.CopyFrom(value); + if (value.wrapped == nullptr) { + this->wrapped->clear_value(); + this->value = Value(); + } else { + this->wrapped->mutable_value()->CopyFrom(*value.wrapped); + this->value = Value(this->wrapped->mutable_value()); + } + return StatusCode::SUCCESS; } +auto Entity::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); } + auto Entity::SetValue(const std::string &value) -> StatusCode { return SetValue(Value(value)); } auto Entity::SetValue(const char *value) -> StatusCode { return SetValue(Value(value)); } @@ -235,6 +227,10 @@ auto Entity::SetValue(const std::vector<int64_t> &values) -> StatusCode { return SetValue(Value(values)); } +auto Entity::SetValue(const std::vector<int> &values) -> StatusCode { + return SetValue(Value(values)); +} + auto Entity::SetValue(const std::vector<double> &values) -> StatusCode { return SetValue(Value(values)); } @@ -245,6 +241,8 @@ auto Entity::SetValue(const std::vector<bool> &values) -> StatusCode { auto Entity::SetValue(const int64_t value) -> StatusCode { return SetValue(Value(value)); } +auto Entity::SetValue(const int value) -> StatusCode { return SetValue(Value(value)); } + auto Entity::SetValue(const bool value) -> StatusCode { return SetValue(Value(value)); } auto Entity::SetUnit(const std::string &unit) -> void { this->wrapped->set_unit(unit); } @@ -253,7 +251,14 @@ auto Entity::SetDataType(const DataType &new_data_type) -> StatusCode { if (GetRole() != Role::PROPERTY) { return StatusCode::ENTITY_CANNOT_HAVE_A_DATA_TYPE; } - return this->data_type.CopyFrom(new_data_type); + if (new_data_type.wrapped == nullptr) { + this->wrapped->clear_data_type(); + this->data_type = DataType(); + } else { + this->wrapped->mutable_data_type()->CopyFrom(*new_data_type.wrapped); + this->data_type = DataType(this->wrapped->mutable_data_type()); + } + return StatusCode::SUCCESS; } auto Entity::SetDataType(const AtomicDataType new_data_type, bool list_type) -> StatusCode { @@ -268,99 +273,4 @@ auto Entity::SetFilePath(const std::string &path) -> void { this->wrapped->mutable_file_descriptor()->set_path(path); } -auto Entity::FixValue() -> void { FixValueImpl(this); } - -// Utility functions ////////////////////////////////////////////////////////// - -// TODO(daniel) cognitive complexity is too high -template <typename E> auto FixValueImpl(E *ent) -> void { //NOLINT - const auto &dtype = ent->GetDataType(); - const auto &value = ent->GetValue(); - auto new_value = Value(); - if (value.IsNull() || !value.IsString()) { // Don't treat NULL and non-string values. - return; - } - if (value.IsList()) { // Also don't treat empty or non-string lists. - const auto &list = value.AsList(); - if (list.empty() || !list[0].IsString()) { - return; - } - } - auto atype = AtomicDataType::UNSPECIFIED; - if (dtype.IsList()) { // List Datatype - if (!value.IsList()) { - throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR, - "DataType is list, but Value is scalar."); - } - auto &list_type = dtype.AsList(); - atype = list_type.GetAtomicDataType(); - if (!list_type.IsListOfAtomic() // References, strings etc. need no treatment. - || atype == AtomicDataType::UNSPECIFIED || atype == AtomicDataType::TEXT || - atype == AtomicDataType::DATETIME) { - return; - } - if (atype == AtomicDataType::DOUBLE) { - std::vector<double> data; - for (auto &d : value.AsList()) { - data.push_back(std::stod(d.AsString())); - } - new_value = Value(data); - } else if (atype == AtomicDataType::INTEGER) { - std::vector<int64_t> data; - for (auto &d : value.AsList()) { - data.push_back(std::stol(d.AsString())); - } - new_value = Value(data); - } else if (atype == AtomicDataType::BOOLEAN) { - std::vector<bool> data; - for (auto &d : value.AsList()) { - const auto &bool_value = d.AsString(); - if (boost::to_upper_copy(bool_value) == "TRUE") { - data.push_back(true); - } else if (boost::to_upper_copy(bool_value) == "FALSE") { - data.push_back(false); - } else { - throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR, - "Boolean value is neither true nor false."); - } - } - new_value = Value(data); - } else { - std::cout << "Unhandled datatype: " << ent->ToString() << std::endl; - throw std::logic_error("Unhandled datatype"); - } - } else { // Scalar Datatype - if (value.IsList()) { - throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR, - "Value is list, but DataType is scalar."); - } - atype = dtype.AsAtomic(); - if (!dtype.IsAtomic() // References, strings etc. need no treatment. - || atype == AtomicDataType::UNSPECIFIED || atype == AtomicDataType::TEXT || - atype == AtomicDataType::DATETIME) { - return; - } - if (atype == AtomicDataType::DOUBLE) { - new_value = Value(std::stod(value.AsString())); - } else if (atype == AtomicDataType::INTEGER) { - new_value = Value(std::stol(value.AsString())); - } else if (atype == AtomicDataType::BOOLEAN) { - const auto &bool_value = value.AsString(); - if (boost::to_upper_copy(bool_value) == "TRUE") { - new_value = Value(true); - } else if (boost::to_upper_copy(bool_value) == "FALSE") { - new_value = Value(false); - } else { - throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR, - "Boolean value is neither true nor false."); - } - } else { - std::cout << "Unhandled datatype: " << ent->ToString() << std::endl; - throw std::logic_error("Unhandled datatype"); - } - } - - ent->SetValue(new_value); -} - } // namespace caosdb::entity diff --git a/src/caosdb/file_transmission/download_request_handler.cpp b/src/caosdb/file_transmission/download_request_handler.cpp index a157754b337e798fc4c144cb44abea153053ed79..cbe79e93eb89261d1d85484cf39f4ad5deecba18 100644 --- a/src/caosdb/file_transmission/download_request_handler.cpp +++ b/src/caosdb/file_transmission/download_request_handler.cpp @@ -59,17 +59,16 @@ #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... #include <exception> // IWYU pragma: keep // IWYU pragma: no_include <bits/exception.h> -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for Arena -#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncRe... -#include <grpcpp/impl/codegen/client_context.h> // for ClientContext -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <grpcpp/impl/codegen/status.h> // for Status -#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... -#include <iostream> // for char_traits -#include <stdexcept> // for runtime_error -#include <string> // for string, opera... -#include <utility> // for move +#include <google/protobuf/arena.h> // for Arena +#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncRe... +#include <grpcpp/impl/codegen/client_context.h> // for ClientContext +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <grpcpp/impl/codegen/status.h> // for Status +#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... +#include <iostream> // for char_traits +#include <stdexcept> // for runtime_error +#include <string> // for string, opera... +#include <utility> // for move namespace caosdb::transaction { using caosdb::StatusCode; diff --git a/src/caosdb/file_transmission/file_reader.cpp b/src/caosdb/file_transmission/file_reader.cpp index f118eca01c6d17975684a15de56eb7c693fdc117..1a78e5a76513ae0aaf94b2ae4967af5d7b66e0c0 100644 --- a/src/caosdb/file_transmission/file_reader.cpp +++ b/src/caosdb/file_transmission/file_reader.cpp @@ -59,7 +59,7 @@ FileReader::FileReader(boost::filesystem::path filename) } void FileReader::openFile() { - stream_.open(filename_, std::ios::binary | std::ios::ate); + stream_.open(filename_.generic_string(), std::ios::binary | std::ios::ate); if (!stream_) { throw FileIOError("Can't open file for reading: " + filename_.string()); } diff --git a/src/caosdb/file_transmission/file_writer.cpp b/src/caosdb/file_transmission/file_writer.cpp index 5cf7d3a798942fc2ab9a3fe4a278b0e29d362f6e..2c7f2a6000718366f846a4be61dd5c2144370a65 100644 --- a/src/caosdb/file_transmission/file_writer.cpp +++ b/src/caosdb/file_transmission/file_writer.cpp @@ -58,7 +58,7 @@ FileWriter::FileWriter(boost::filesystem::path filename) : filename_(std::move(f } void FileWriter::openFile() { - stream_.open(filename_, std::ios::binary | std::ios::trunc); + stream_.open(filename_.generic_string(), std::ios::binary | std::ios::trunc); if (!stream_) { throw FileIOError("Can't open file for writing: " + filename_.string()); } diff --git a/src/caosdb/file_transmission/upload_request_handler.cpp b/src/caosdb/file_transmission/upload_request_handler.cpp index cc8170249b932fb8bfa572fd345746bbbacc16af..bc74391753a0c7c3dce01f80d8fa67acc9e782cd 100644 --- a/src/caosdb/file_transmission/upload_request_handler.cpp +++ b/src/caosdb/file_transmission/upload_request_handler.cpp @@ -61,17 +61,16 @@ #include <cstdint> // for uint64_t #include <exception> // IWYU pragma: keep // IWYU pragma: no_include <bits/exception.h> -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... -#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncWr... -#include <grpcpp/impl/codegen/call_op_set.h> // for WriteOptions -#include <grpcpp/impl/codegen/client_context.h> // for ClientContext -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <grpcpp/impl/codegen/status.h> // for Status -#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... -#include <iostream> // for endl, streamsize -#include <string> // for basic_string -#include <utility> // for move +#include <google/protobuf/arena.h> // for Arena +#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncWr... +#include <grpcpp/impl/codegen/call_op_set.h> // for WriteOptions +#include <grpcpp/impl/codegen/client_context.h> // for ClientContext +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <grpcpp/impl/codegen/status.h> // for Status +#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... +#include <iostream> // for endl, streamsize +#include <string> // for basic_string +#include <utility> // for move namespace caosdb::transaction { using caosdb::StatusCode; diff --git a/src/caosdb/protobuf_helper.cpp b/src/caosdb/protobuf_helper.cpp index e8bbd07834ead9b561c7e8769ed834527337f7a6..418d14b9c847bc204582f6165fae81bf6adcc156 100644 --- a/src/caosdb/protobuf_helper.cpp +++ b/src/caosdb/protobuf_helper.cpp @@ -19,8 +19,7 @@ * */ #include "caosdb/protobuf_helper.h" -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/extension_set.h> // for Arena +#include <google/protobuf/arena.h> // for Arena namespace caosdb::utility { diff --git a/src/caosdb/status_code_description.cpp b/src/caosdb/status_code_description.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5e08e7820ca0f7380723087570f838465a9c261 --- /dev/null +++ b/src/caosdb/status_code_description.cpp @@ -0,0 +1,175 @@ +/* + * This file is a part of the CaosDB Project. + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "caosdb/status_code.h" // for StatusCode, ABORTED, ALREADY_EXISTS +#include <map> // for allocator, map +#include <stdexcept> // for out_of_range +#include <string> // for string, basic_string + +namespace caosdb { + +/* + * The descriptions of the StatusCodes 1-16 are originally taken from + * https://github.com/grpc/grpc/blob/ce5b4e949fc75ed4c19e9ccfec7dc95c8ee9ae45/doc/statuscodes.md + * and adapted to our purposes. + * + * They are origially released under an Apache-2.0 license in the linked + * repository. However, the linked file itself does not contain a copyright or + * license statement and so we have to assume that the copyright holders are an + * unknown subset of the contributers of that repository: + * + * Copyright (C) 2015-2021 unknown gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +auto get_status_description(int code) -> const std::string & { + static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION"; + static const std::map<int, std::string> descriptions = { + {StatusCode::INITIAL, "The transaction has just been initialized. It has not been executed yet " + "and clients can still change it and add sub-transactions."}, + {StatusCode::GO_ON, "The transaction has a transaction_type yet and clients may add matching " + "sub-transaction or execute it right-away."}, + {StatusCode::READY, "The transaction is ready for execution and cannot be changed anymore."}, + {StatusCode::EXECUTING, "The transaction is currently being executed."}, + {StatusCode::SUCCESS, "The action was successful"}, + + {StatusCode::CANCELLED, "The operation was canceled (typically by the caller)."}, + {StatusCode::UNKNOWN, "Unknown error. This is typically a bug (server or client)."}, + {StatusCode::INVALID_ARGUMENT, + "Client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. " + "INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the " + "system (e.g., a malformed file name)."}, + {StatusCode::DEADLINE_EXCEEDED, + "Deadline expired before operation could complete. For operations that change the state of " + "the system, this error may be returned even if the operation has completed successfully. For " + "example, a successful response from a server could have been delayed long enough for the " + "deadline to expire."}, + {StatusCode::NOT_FOUND, "Some requested entity (e.g., file or directory) was not found."}, + {StatusCode::ALREADY_EXISTS, + "Some entity that we attempted to create (e.g., file or directory) already exists."}, + {StatusCode::PERMISSION_DENIED, + "The caller does not have permission to execute the specified operation. PERMISSION_DENIED " + "must not be used for rejections caused by exhausting some resource (use RESOURCE_EXHAUSTED " + "instead for those errors). PERMISSION_DENIED must not be used if the caller can not be " + "identified (use UNAUTHENTICATED instead for those errors)."}, + {StatusCode::RESOURCE_EXHAUSTED, "Some resource has been exhausted, perhaps a per-user quota, " + "or perhaps the entire file system is out of space."}, + {StatusCode::FAILED_PRECONDITION, + "Operation was rejected because the system is not in a state required for the operation's " + "execution. For example, directory to be deleted may be non-empty, an rmdir operation is " + "applied to a non-directory, etc. A litmus test that may help a service implementor in " + "deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: (a) Use UNAVAILABLE if the " + "client can retry just the failing call. (b) Use ABORTED if the client should retry at a " + "higher-level (e.g., restarting a read-modify-write sequence). (c) Use FAILED_PRECONDITION if " + "the client should not retry until the system state has been explicitly fixed. E.g., if an " + "'rmdir' fails because the directory is non-empty, FAILED_PRECONDITION should be returned " + "since the client should not retry unless they have first fixed up the directory by deleting " + "files from it. (d) Use FAILED_PRECONDITION if the client performs conditional REST " + "Get/Update/Delete on a resource and the resource on the server does not match the condition. " + "E.g., conflicting read-modify-write on the same resource."}, + {StatusCode::ABORTED, + "The operation was aborted, typically due to a concurrency issue like sequencer check " + "failures, transaction aborts, etc. See litmus test above for deciding between " + "FAILED_PRECONDITION, ABORTED, and UNAVAILABLE."}, + {StatusCode::OUT_OF_RANGE, + "Operation was attempted past the valid range. E.g., seeking or reading past end of file. " + "Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system " + "state changes. For example, a 32-bit file system will generate INVALID_ARGUMENT if asked to " + "read at an offset that is not in the range [0,2^32-1], but it will generate OUT_OF_RANGE if " + "asked to read from an offset past the current file size. There is a fair bit of overlap " + "between FAILED_PRECONDITION and OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more " + "specific error) when it applies so that callers who are iterating through a space can easily " + "look for an OUT_OF_RANGE error to detect when they are done."}, + {StatusCode::UNIMPLEMENTED, + "Operation is not implemented or not supported/enabled in this service. In contrast to " + "UNSUPPORTED_FEATURE (which is a client error) this error indicates that the server does not " + "support this operation."}, + {StatusCode::INTERNAL, + "Internal errors. Means some invariants expected by underlying System has been broken. If you " + "see one of these errors, Something is very broken. "}, + {StatusCode::DATA_LOSS, "Unrecoverable data loss or corruption."}, + + {StatusCode::CONNECTION_ERROR, + "The attempt to execute this transaction was not successful because the " + "connection to the server could not be established."}, + {StatusCode::AUTHENTICATION_ERROR, + "The attempt to execute this transaction has not been executed at all " + "because the authentication did not succeed."}, + {StatusCode::GENERIC_RPC_ERROR, + "The attempt to execute this transaction was not successful because an " + "error occured in the transport or RPC protocol layer."}, + {StatusCode::GENERIC_ERROR, "An error occured. Please review the logs for more information."}, + {StatusCode::GENERIC_TRANSACTION_ERROR, + "The transaction terminated unsuccessfully with transaction errors."}, + {StatusCode::CONFIGURATION_ERROR, + "An error occurred during the configuration of the ConfigurationManager."}, + {StatusCode::UNKNOWN_CONNECTION_ERROR, + "The ConnectionManager does not know any connection of this name."}, + {StatusCode::TRANSACTION_STATUS_ERROR, + "The Transaction is in a wrong state for the attempted action."}, + {StatusCode::TRANSACTION_TYPE_ERROR, + "The Transaction has a transaction type which does not allow the " + "attempted action."}, + {StatusCode::ORIGINAL_ENTITY_MISSING_ID, + "The attempt to update this entity failed because this entity does not " + "have " + "an id. This is the case when you did not retrieve it before applying any " + "changes and instantiated the Entity class explicitly."}, + {StatusCode::NOT_A_FILE_ENTITY, "You can only add files to file entities."}, + {StatusCode::PATH_IS_A_DIRECTORY, "The given path is a directory."}, + {StatusCode::FILE_DOES_NOT_EXIST_LOCALLY, + "The file does not not exist in the local file system."}, + {StatusCode::FILE_DOWNLOAD_ERROR, "The transaction failed during the download of the files"}, + {StatusCode::FILE_UPLOAD_ERROR, "The transaction failed during the upload of the files"}, + {StatusCode::UNSUPPORTED_FEATURE, + "This feature is not available in the this client implementation."}, + {StatusCode::EXTERN_C_ASSIGNMENT_ERROR, + "You tried to assign a new object to the wrapped void pointer. You have " + "to delete the old pointee first."}, + {StatusCode::ENUM_MAPPING_ERROR, + "The role, importance, or datatype you specified does not exist."}, + {StatusCode::SPOILED, + "The object has been used in a way that left it in a corrupted state. This does not indicate " + "any form of misuse. It just indicates that the object is spoiled for further use now."}, + {StatusCode::OTHER_CLIENT_ERROR, + "This is code is reserved to errors raised by other clients wrapping the " + "C++ client (or its Extern C interface). This should never occur when " + "working with the C++ code itself."}}; + try { + return descriptions.at(code); + } catch (const std::out_of_range &exc) { + return MISSING_DESCRIPTION; + } +} + +} // namespace caosdb + +// LocalWords: ConnectionManager Extern diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index 58bf54e92554f4660293a869954e9e3482eebdf7..002d20d792b49bfb88b160cb39e7d8a8e6a5bd01 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -35,81 +35,14 @@ #include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_... #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... // IWYU pragma: no_include <bits/exception.h> -#include <exception> // IWYU pragma: keep -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... -#include <grpc/impl/codegen/gpr_types.h> // for gpr_timespec -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <iosfwd> // for streamsize -#include <map> // for map, operator!= -#include <memory> // for unique_ptr -#include <stdexcept> // for out_of_range -#include <utility> // for move, pair - -namespace caosdb { - -// TODO(tf) move to another source file. -auto get_status_description(int code) -> const std::string & { - static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION"; - static const std::map<int, std::string> descriptions = { - {StatusCode::INITIAL, "The transaction has just been intialized. It has not been executed yet " - "and clients can still change it and add sub-transactions."}, - {StatusCode::GO_ON, "The transaction has a transaction_type yet and clients may add matching " - "sub-transaction or execute it right-away."}, - {StatusCode::READY, "The transaction is ready for execution and cannot be changed anymore."}, - {StatusCode::EXECUTING, "The transaction is currently being executed."}, - {StatusCode::SUCCESS, "The action was successful"}, - {StatusCode::CONNECTION_ERROR, - "The attempt to execute this transaction was not successful because the " - "connection to the server could not be established."}, - {StatusCode::AUTHENTICATION_ERROR, - "The attempt to execute this transaction has not been executed at all " - "because the authentication did not succeed."}, - {StatusCode::GENERIC_RPC_ERROR, - "The attempt to execute this transaction was not successful because an " - "error occured in the transport or RPC protocol layer."}, - {StatusCode::GENERIC_ERROR, "An error occured. Please review the logs for more information."}, - {StatusCode::GENERIC_TRANSACTION_ERROR, - "The transaction terminated unsuccessfully with transaction errors."}, - {StatusCode::CONFIGURATION_ERROR, - "An error occurred during the configuration of the ConfigurationManager."}, - {StatusCode::UNKNOWN_CONNECTION_ERROR, - "The ConnectionManager does not know any connection of this name."}, - {StatusCode::TRANSACTION_STATUS_ERROR, - "The Transaction is in a wrong state for the attempted action."}, - {StatusCode::TRANSACTION_TYPE_ERROR, - "The Transaction has a transaction type which does not allow the " - "attempted action."}, - {StatusCode::ORIGINAL_ENTITY_MISSING_ID, - "The attempt to update this entity failed because this entity does not " - "have " - "an id. This is the case when you did not retrieve it before applying any " - "changes and instantiated the Entity class explicitely."}, - {StatusCode::NOT_A_FILE_ENTITY, "You can only add files to file entities."}, - {StatusCode::PATH_IS_A_DIRECTORY, "The given path is a directory."}, - {StatusCode::FILE_DOES_NOT_EXIST_LOCALLY, - "The file does not not exist in the local file system."}, - {StatusCode::FILE_DOWNLOAD_ERROR, "The transaction failed during the download of the files"}, - {StatusCode::FILE_UPLOAD_ERROR, "The transaction failed during the upload of the files"}, - {StatusCode::UNSUPPORTED_FEATURE, - "This feature is not available in the this client implementation."}, - {StatusCode::EXTERN_C_ASSIGNMENT_ERROR, - "You tried to assign a new object to the wrapped void pointer. You have " - "to delete the old pointee first."}, - {StatusCode::ENUM_MAPPING_ERROR, - "The role, importance, or datatype you specified does not exist."}, - {StatusCode::OTHER_CLIENT_ERROR, - "This is code is reserved to errors raised by other clients wrapping the " - "C++ client (or its Extern C interface). This should never occur when " - "working with the C++ code itself."}}; - try { - return descriptions.at(code); - } catch (const std::out_of_range &exc) { - return MISSING_DESCRIPTION; - } -} - -} // namespace caosdb +#include <exception> // IWYU pragma: keep +#include <google/protobuf/arena.h> // for Arena +#include <grpc/impl/codegen/gpr_types.h> // for gpr_timespec +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <iosfwd> // for streamsize +#include <map> // for map, operator!= +#include <memory> // for unique_ptr +#include <utility> // for move, pair namespace caosdb::transaction { using caosdb::entity::v1alpha1::EntityTransactionService; @@ -224,7 +157,7 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode { entity->SetFileTransmissionId(file_transmission_id); upload_files.push_back(entity->GetFileDescriptor()); } - this->status = TransactionStatus::READY(); + this->status = TransactionStatus::GO_ON(); return this->status.GetCode(); } @@ -241,7 +174,7 @@ auto Transaction::UpdateEntity(Entity *entity) noexcept -> StatusCode { entity->SetFileTransmissionId(file_transmission_id); upload_files.push_back(entity->GetFileDescriptor()); } - this->status = TransactionStatus::READY(); + this->status = TransactionStatus::GO_ON(); return this->status.GetCode(); } @@ -260,12 +193,6 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode { // NOLINT return StatusCode::TRANSACTION_STATUS_ERROR; } switch (this->transaction_type) { - case MIXED_WRITE: - CAOSDB_LOG_ERROR_AND_RETURN_STATUS( - logger_name, StatusCode::UNSUPPORTED_FEATURE, - "MIXED_WRITE UNSUPPORTED: The current implementation does not support " - "mixed write transactions (containing insertions, deletions, and updates " - "in one transaction).") case MIXED_READ_AND_WRITE: CAOSDB_LOG_ERROR_AND_RETURN_STATUS( logger_name, StatusCode::UNSUPPORTED_FEATURE, diff --git a/src/caosdb/unary_rpc_handler.cpp b/src/caosdb/unary_rpc_handler.cpp index 14b2971d8b0e693d68f2e9cf35874ba4fd229e86..9a61a534ec30da943f7637d9995a2595acf96804 100644 --- a/src/caosdb/unary_rpc_handler.cpp +++ b/src/caosdb/unary_rpc_handler.cpp @@ -112,7 +112,7 @@ void UnaryRpcHandler::handleCallCompleteState() { switch (status_.error_code()) { case grpc::OK: - CAOSDB_LOG_INFO(logger_name) << "UnaryRpcHandler finished successfully."; + CAOSDB_LOG_TRACE(logger_name) << "UnaryRpcHandler finished successfully."; break; default: auto code(static_cast<StatusCode>(status_.error_code())); diff --git a/src/caosdb/utility.cpp b/src/caosdb/utility.cpp index d049b91c4ce06337de57f24715df983e80ba88e3..a4ed293adb6bd4fbfb10860e03185aeb567de163 100644 --- a/src/caosdb/utility.cpp +++ b/src/caosdb/utility.cpp @@ -18,10 +18,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ -#include "caosdb/data_type.h" -#include "caosdb/entity.h" #include "caosdb/utility.h" -#include <algorithm> +#include "caosdb/data_type.h" // for AtomicDataType, atomicdatatype_names +#include "caosdb/entity.h" // for Importance, Role, importance_names +#include <map> // for map, operator!=, _Rb_tree_const_iterator +#include <utility> // for pair namespace caosdb::utility { diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp index c506e446eab6f4491af12884a01ca4c8814ae649..853b3c179f082c6afdae8e2126bf52e69d34e680 100644 --- a/src/ccaosdb.cpp +++ b/src/ccaosdb.cpp @@ -23,6 +23,8 @@ #include "caosdb/connection.h" #include "caosdb/constants.h" #include "caosdb/data_type.h" // for DataType, AtomicDat... +#include "caosdb/entity.h" +#include "caosdb/value.h" #include "caosdb/utility.h" #include "caosdb/status_code.h" #include "caosdb/logging.h" @@ -46,12 +48,21 @@ extern "C" { #define WRAPPED_MESSAGE_CAST(name) static_cast<caosdb::entity::Message *>(name->wrapped_message) +#define WRAPPED_DATATYPE_CAST(name) static_cast<caosdb::entity::DataType *>(name->wrapped_datatype) + +#define WRAPPED_VALUE_CAST(name) static_cast<caosdb::entity::AbstractValue *>(name->wrapped_value) + #define ENUM_NAME_FROM_VALUE(arg, etype) \ caosdb::utility::getEnumNameFromValue<caosdb::entity::etype>(arg) #define ENUM_VALUE_FROM_NAME(arg, etype) \ caosdb::utility::getEnumValueFromName<caosdb::entity::etype>(arg) +#define RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(name) \ + if (name->_deletable) { \ + return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR; \ + } + /* * Macro for wrapping every function into a try-catch clause. If an exception * occurs, the given StatusCode is being returned. @@ -146,6 +157,58 @@ extern "C" { body_part return 0; \ }) +/** + * Macro for scalar value creators + */ +#define CREATE_VALUE(fname, arg) \ + ERROR_RETURN_CODE(GENERIC_ERROR, \ + int caosdb_entity_create_##fname(caosdb_entity_value *out, arg), { \ + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) \ + out->wrapped_value = new caosdb::entity::Value(value); \ + out->_deletable = true; \ + return 0; \ + }) +/** + * Macro for list-value creators + */ +#define CREATE_VECTOR_VALUE(fname, type, arg, assign) \ + ERROR_RETURN_CODE( \ + GENERIC_ERROR, \ + int caosdb_entity_create_##fname(caosdb_entity_value *out, arg, const int length), { \ + if (out->_deletable) { \ + return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR; \ + } \ + std::vector<type> value_vec; \ + for (int i = 0; i < length; i++) { \ + value_vec.push_back(assign); \ + } \ + out->wrapped_value = new caosdb::entity::Value(value_vec); \ + out->_deletable = true; \ + return 0; \ + }) + +/** + * Macro for value is-a functions + */ +#define VALUE_IS(fname, isfunction) \ + ERROR_RETURN_CODE(GENERIC_ERROR, \ + int caosdb_entity_value_is_##fname(caosdb_entity_value *value, bool *out), { \ + auto *wrapped_value = WRAPPED_VALUE_CAST(value); \ + *out = wrapped_value->isfunction(); \ + return 0; \ + }) + +/** + * Macro for some value getters + */ +#define VALUE_GET_AS(fname, getfunction, arg) \ + ERROR_RETURN_CODE(GENERIC_ERROR, \ + int caosdb_entity_value_get_as_##fname(caosdb_entity_value *value, arg), { \ + auto *wrapped_value = WRAPPED_VALUE_CAST(value); \ + *out = wrapped_value->getfunction(); \ + return 0; \ + }) + int caosdb_constants_LIBCAOSDB_VERSION_MAJOR() { return caosdb::LIBCAOSDB_VERSION_MAJOR; } int caosdb_constants_LIBCAOSDB_VERSION_MINOR() { return caosdb::LIBCAOSDB_VERSION_MINOR; } @@ -170,8 +233,8 @@ const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE() { int caosdb_status_code_OTHER_CLIENT_ERROR() { return caosdb::StatusCode::OTHER_CLIENT_ERROR; } -const char *caosdb_utility_get_env_var(const char *name, const char *fallback) { - return caosdb::utility::get_env_var(name, fallback); +const char *caosdb_utility_get_env_fallback(const char *name, const char *fallback) { + return caosdb::utility::get_env_fallback(name, fallback); } const char *caosdb_get_status_description(int code) { @@ -501,6 +564,32 @@ ERROR_RETURN_CODE( return 0; }) +ERROR_RETURN_CODE( + GENERIC_ERROR, + int caosdb_transaction_transaction_release_result_set(caosdb_transaction_transaction *transaction, + caosdb_transaction_result_set *out), + { + if (out->_deletable) { + return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR; + } + auto *wrapped_transaction = + static_cast<caosdb::transaction::Transaction *>(transaction->wrapped_transaction); + out->wrapped_result_set = (void *)(wrapped_transaction->ReleaseResultSet()); + // out is the owner now, that are the semantics of ReleaseResultSet + out->_deletable = true; + return 0; + }) + +ERROR_RETURN_CODE( + GENERIC_ERROR, + int caosdb_transaction_delete_result_set(caosdb_transaction_result_set *result_set), { + if (result_set->_deletable) { + delete static_cast<caosdb::entity::Entity *>(result_set->wrapped_result_set); + } + result_set->_deletable = false; + return 0; + }) + ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_transaction_transaction_get_count_result( caosdb_transaction_transaction *transaction, long *out), @@ -522,6 +611,22 @@ ERROR_RETURN_CODE(GENERIC_ERROR, return 0; }) +ERROR_RETURN_CODE( + GENERIC_ERROR, + int caosdb_transaction_result_set_release_at(caosdb_transaction_result_set *result_set, + caosdb_entity_entity *entity, int index), + { + if (entity->_deletable) { + return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR; + } + auto *wrapped_result_set = + static_cast<caosdb::transaction::MultiResultSet *>(result_set->wrapped_result_set); + entity->wrapped_entity = wrapped_result_set->release_at(index); + // entity is the owner now. That are the semantics of release_at. + entity->_deletable = true; + return 0; + }) + ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_transaction_result_set_size(caosdb_transaction_result_set *result_set, int *out), @@ -584,6 +689,82 @@ ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_delete_parent(caosdb_entity_p return 0; }) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_create_atomic_datatype(caosdb_entity_datatype *out, + const char *name), + { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) + try { + auto enum_value = ENUM_VALUE_FROM_NAME(std::string(name), AtomicDataType); + out->wrapped_datatype = new caosdb::entity::DataType(enum_value); + out->_deletable = true; + return 0; + } catch (const std::out_of_range &exc) { + caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what()); + return caosdb::StatusCode::ENUM_MAPPING_ERROR; + } + }) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_create_reference_datatype(caosdb_entity_datatype *out, + const char *name), + { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) + out->wrapped_datatype = new caosdb::entity::DataType(std::string(name)); + out->_deletable = true; + return 0; + }) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_create_atomic_list_datatype(caosdb_entity_datatype *out, + const char *name), + { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) + try { + auto enum_value = ENUM_VALUE_FROM_NAME(std::string(name), AtomicDataType); + out->wrapped_datatype = + new caosdb::entity::DataType(caosdb::entity::DataType::ListOf(enum_value)); + out->_deletable = true; + return 0; + } catch (const std::out_of_range &exc) { + caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what()); + return caosdb::StatusCode::ENUM_MAPPING_ERROR; + } + }) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_create_reference_list_datatype(caosdb_entity_datatype *out, + const char *name), + { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) + out->wrapped_datatype = new caosdb::entity::DataType( + caosdb::entity::DataType::ListOf(std::string(name))); + out->_deletable = true; + return 0; + }) + +ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_delete_datatype(caosdb_entity_datatype *out), { + if (out->_deletable) { + delete WRAPPED_DATATYPE_CAST(out); + } + out->_deletable = false; + return 0; +}) + +CREATE_VALUE(int_value, const int64_t value) +CREATE_VALUE(string_value, const char *value) +CREATE_VALUE(double_value, const double value) +CREATE_VALUE(bool_value, const bool value) +CREATE_VECTOR_VALUE(int_vector_value, int64_t, const int64_t *value, value[i]) +CREATE_VECTOR_VALUE(string_vector_value, std::string, const char **value, std::string(value[i])) +CREATE_VECTOR_VALUE(double_vector_value, double, const double *value, value[i]) +CREATE_VECTOR_VALUE(bool_vector_value, bool, const bool *value, value[i]) + +ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_delete_value(caosdb_entity_value *out), { + if (out->_deletable) { + delete WRAPPED_VALUE_CAST(out); + } + out->_deletable = false; + return 0; +}) + CAOSDB_ENTITY_GET(id, GetId()) ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_entity_get_role(caosdb_entity_entity *entity, char **out), { @@ -610,111 +791,23 @@ ERROR_RETURN_CODE(GENERIC_ERROR, // CAOSDB_ENTITY_GET(file_path, GetFilePath()) TODO(henrik) CAOSDB_ENTITY_GET(description, GetDescription()) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, char **name, - bool *is_ref, bool *is_list), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - const auto &datatype = wrapped_entity->GetDataType(); - *is_list = datatype.IsList(); - std::string datatype_name; - if (*is_list) { - const auto &list_datatype = datatype.AsList(); - *is_ref = list_datatype.IsListOfReference(); - if (*is_ref) { - datatype_name = list_datatype.GetReferenceDataType().GetName(); - } else { - datatype_name = - ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(), AtomicDataType); - } - } else { - *is_ref = datatype.IsReference(); - if (*is_ref) { - datatype_name = datatype.AsReference().GetName(); - } else { - datatype_name = ENUM_NAME_FROM_VALUE(datatype.AsAtomic(), AtomicDataType); - } - } - char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1); - strcpy(tmp, datatype_name.c_str()); - delete[] * name; - *name = tmp; - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_int_value(caosdb_entity_entity *entity, long *out), { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - *out = wrapped_entity->GetValue().AsInteger(); - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_double_value(caosdb_entity_entity *entity, - double *out), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - *out = wrapped_entity->GetValue().AsDouble(); - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_boolean_value(caosdb_entity_entity *entity, - bool *out), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - *out = wrapped_entity->GetValue().AsBool(); - return 0; - }) -CAOSDB_ENTITY_GET(string_value, GetValue().AsString()) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_int_list_value_at(caosdb_entity_entity *entity, - long *out, const int index), + int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, + caosdb_entity_datatype *out), { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - auto value_list = wrapped_entity->GetValue().AsList(); - *out = value_list[index].AsInteger(); - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_double_list_value_at(caosdb_entity_entity *entity, - double *out, const int index), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - auto value_list = wrapped_entity->GetValue().AsList(); - *out = value_list[index].AsDouble(); - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_boolean_value_list_value_at( - caosdb_entity_entity *entity, bool *out, const int index), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - auto value_list = wrapped_entity->GetValue().AsList(); - *out = value_list[index].AsBool(); - return 0; - }) -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_string_list_value_at(caosdb_entity_entity *entity, - char **out, const int index), - { - auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - auto value_list = wrapped_entity->GetValue().AsList(); - char *tmp = - (char *)malloc(sizeof(char) * value_list[index].AsString().length() + 1); - strcpy(tmp, value_list[index].AsString().c_str()); - delete[] * out; - *out = tmp; + out->wrapped_datatype = (void *)(&(wrapped_entity->GetDataType())); + out->_deletable = false; return 0; }) - ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_get_value_list_length(caosdb_entity_entity *entity, - int *out), + int caosdb_entity_entity_get_value(caosdb_entity_entity *entity, + caosdb_entity_value *out), { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - if (wrapped_entity->GetValue().IsList()) { - *out = wrapped_entity->GetValue().AsList().size(); - } else { - *out = 0; - } + out->wrapped_value = (void *)(&(wrapped_entity->GetValue())); + out->_deletable = false; return 0; }) @@ -858,140 +951,156 @@ ERROR_RETURN_CODE(GENERIC_ERROR, *out = tmp; return 0; }) - ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_property_get_datatype(caosdb_entity_property *property, - char **name, bool *is_ref, bool *is_list), + caosdb_entity_datatype *out), { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - const auto &datatype = wrapped_property->GetDataType(); - *is_list = datatype.IsList(); - std::string datatype_name; - if (*is_list) { - const auto &list_datatype = datatype.AsList(); - *is_ref = list_datatype.IsListOfReference(); - if (*is_ref) { - datatype_name = list_datatype.GetReferenceDataType().GetName(); - } else { - datatype_name = - ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(), AtomicDataType); - } - } else { - *is_ref = datatype.IsReference(); - if (*is_ref) { - datatype_name = datatype.AsReference().GetName(); - } else { - datatype_name = ENUM_NAME_FROM_VALUE(datatype.AsAtomic(), AtomicDataType); - } - } - char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1); - strcpy(tmp, datatype_name.c_str()); - delete[] * name; - *name = tmp; - return 0; - }) - -CAOSDB_PROPERTY_GET(unit, GetUnit()) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_int_value(caosdb_entity_property *property, - long *out), - { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - *out = wrapped_property->GetValue().AsInteger(); + out->wrapped_datatype = (void *)(&(wrapped_property->GetDataType())); + out->_deletable = false; return 0; }) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_double_value(caosdb_entity_property *property, - double *out), + int caosdb_entity_property_get_value(caosdb_entity_property *property, + caosdb_entity_value *out), { + RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out) auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - *out = wrapped_property->GetValue().AsDouble(); + out->wrapped_value = (void *)(&(wrapped_property->GetValue())); + out->_deletable = false; return 0; }) +CAOSDB_PROPERTY_GET(unit, GetUnit()) + ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_boolean_value(caosdb_entity_property *property, - bool *out), - { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - *out = wrapped_property->GetValue().AsBool(); + int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out), { + auto *wrapped_message = + static_cast<caosdb::entity::Message *>(message->wrapped_message); + *out = wrapped_message->GetCode(); return 0; }) -CAOSDB_PROPERTY_GET(string_value, GetValue().AsString()) + +ERROR_RETURN_CODE( + GENERIC_ERROR, + int caosdb_entity_message_get_description(caosdb_entity_message *message, char **out), { + auto *wrapped_message = static_cast<caosdb::entity::Message *>(message->wrapped_message); + auto *tmp = (char *)malloc(sizeof(char) * wrapped_message->GetDescription().length() + 1); + strcpy(tmp, wrapped_message->GetDescription().c_str()); + delete[] * out; + *out = tmp; + return 0; + }) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_int_list_value_at(caosdb_entity_property *property, - long *out, const int index), + int caosdb_entity_datatype_is_undefined(caosdb_entity_datatype *datatype, + bool *out), { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - auto value_list = wrapped_property->GetValue().AsList(); - *out = value_list[index].AsInteger(); + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + *out = wrapped_datatype->IsUndefined(); return 0; }) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_double_list_value_at( - caosdb_entity_property *property, double *out, const int index), + int caosdb_entity_datatype_is_atomic(caosdb_entity_datatype *datatype, bool *out), { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - auto value_list = wrapped_property->GetValue().AsList(); - *out = value_list[index].AsDouble(); + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + *out = wrapped_datatype->IsAtomic(); return 0; }) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_boolean_value_list_value_at( - caosdb_entity_property *property, bool *out, const int index), + int caosdb_entity_datatype_is_reference(caosdb_entity_datatype *datatype, + bool *out), { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - auto value_list = wrapped_property->GetValue().AsList(); - *out = value_list[index].AsBool(); + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + *out = wrapped_datatype->IsReference(); return 0; }) ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_string_list_value_at( - caosdb_entity_property *property, char **out, const int index), + int caosdb_entity_datatype_is_list_of_atomic(caosdb_entity_datatype *datatype, + bool *out), { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - auto value_list = wrapped_property->GetValue().AsList(); - auto *tmp = - (char *)malloc(sizeof(char) * value_list[index].AsString().length() + 1); - strcpy(tmp, value_list[index].AsString().c_str()); - delete[] * out; - *out = tmp; + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + if (wrapped_datatype->IsList()) { + const auto &list_datatype = wrapped_datatype->GetAsList(); + *out = list_datatype.IsListOfAtomic(); + } else { + *out = false; + } return 0; }) - ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_get_value_list_length(caosdb_entity_property *property, - int *out), + int caosdb_entity_datatype_is_list_of_reference(caosdb_entity_datatype *datatype, + bool *out), { - auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - if (wrapped_property->GetValue().IsList()) { - *out = wrapped_property->GetValue().AsList().size(); + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + if (wrapped_datatype->IsList()) { + const auto &list_datatype = wrapped_datatype->GetAsList(); + *out = list_datatype.IsListOfReference(); } else { - *out = 0; + *out = false; } return 0; }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out), { - auto *wrapped_message = - static_cast<caosdb::entity::Message *>(message->wrapped_message); - *out = wrapped_message->GetCode(); - return 0; - }) - ERROR_RETURN_CODE( GENERIC_ERROR, - int caosdb_entity_message_get_description(caosdb_entity_message *message, char **out), { - auto *wrapped_message = static_cast<caosdb::entity::Message *>(message->wrapped_message); - auto *tmp = (char *)malloc(sizeof(char) * wrapped_message->GetDescription().length() + 1); - strcpy(tmp, wrapped_message->GetDescription().c_str()); + int caosdb_entity_datatype_get_datatype_name(caosdb_entity_datatype *datatype, char **out), { + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + std::string datatype_name; + if (wrapped_datatype->IsList()) { + const auto &list_datatype = wrapped_datatype->GetAsList(); + if (list_datatype.IsListOfAtomic()) { + datatype_name = ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(), AtomicDataType); + } else { + datatype_name = list_datatype.GetReferenceDataType().GetName(); + } + } else { + if (wrapped_datatype->IsAtomic()) { + datatype_name = ENUM_NAME_FROM_VALUE(wrapped_datatype->GetAsAtomic(), AtomicDataType); + } else { + datatype_name = wrapped_datatype->GetAsReference().GetName(); + } + } + char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1); + strcpy(tmp, datatype_name.c_str()); delete[] * out; *out = tmp; return 0; }) +VALUE_IS(null, IsNull) +VALUE_IS(string, IsString) +VALUE_IS(double, IsDouble) +VALUE_IS(integer, IsInt64) +VALUE_IS(bool, IsBool) +VALUE_IS(vector, IsVector) + +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_value_get_as_string(caosdb_entity_value *value, char **out), { + auto *wrapped_value = WRAPPED_VALUE_CAST(value); + auto *tmp = + (char *)malloc(sizeof(char) * wrapped_value->GetAsString().length() + 1); + strcpy(tmp, wrapped_value->GetAsString().c_str()); + delete[] * out; + *out = tmp; + return 0; + }) +VALUE_GET_AS(double, GetAsDouble, double *out) +VALUE_GET_AS(integer, GetAsInt64, int64_t *out) +VALUE_GET_AS(bool, GetAsBool, bool *out) +VALUE_GET_AS(vector_size, GetAsVector().size, int *out) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_value_get_as_vector_at(caosdb_entity_value *value, + caosdb_entity_value *out, + const int index), + { + if (out->_deletable) { + return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR; + } + auto *wrapped_value = WRAPPED_VALUE_CAST(value); + out->wrapped_value = (void *)(&(wrapped_value->GetAsVector().at(index))); + out->_deletable = false; + return 0; + }) ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_entity_set_role(caosdb_entity_entity *entity, const char *role), { @@ -1011,128 +1120,24 @@ CAOSDB_ENTITY_SET(local_path, local_path, CAOSDB_ENTITY_SET(file_path, file_path, wrapped_entity->SetFilePath(std::string(file_path));) CAOSDB_ENTITY_SET(description, description, wrapped_entity->SetDescription(std::string(description));) +CAOSDB_ENTITY_SET(unit, unit, wrapped_entity->SetUnit(std::string(unit));) ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity, - const char *datatype, const bool is_ref, - const bool is_list), + caosdb_entity_datatype *datatype), { auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); - if (is_ref) { - // Refernce datatype with name of reference - wrapped_entity->SetDataType(std::string(datatype), is_list); - return 0; - } else { - // Atomic datatype so get from enum - try { - auto enum_value = - ENUM_VALUE_FROM_NAME(std::string(datatype), AtomicDataType); - wrapped_entity->SetDataType(enum_value, is_list); - return 0; - } catch (const std::out_of_range &exc) { - caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what()); - return caosdb::StatusCode::ENUM_MAPPING_ERROR; - } - } - }) -CAOSDB_ENTITY_SET(unit, unit, wrapped_entity->SetUnit(std::string(unit));) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_int_value(caosdb_entity_entity *entity, - const long value), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - wrapped_entity->SetValue(value); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_double_value(caosdb_entity_entity *entity, - const double value), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - wrapped_entity->SetValue(value); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_boolean_value(caosdb_entity_entity *entity, - const bool value), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - wrapped_entity->SetValue(value); - return 0; - }) + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_string_value(caosdb_entity_entity *entity, - const char *value), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - wrapped_entity->SetValue(std::string(value)); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_int_list_value(caosdb_entity_entity *entity, - const long *value, const int length), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - std::vector<long> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_entity->SetValue(value_list); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_double_list_value(caosdb_entity_entity *entity, - const double *value, - const int length), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - std::vector<double> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_entity->SetValue(value_list); - return 0; + return wrapped_entity->SetDataType(*wrapped_datatype); }) - ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_boolean_list_value(caosdb_entity_entity *entity, - const bool *value, - const int length), + int caosdb_entity_entity_set_value(caosdb_entity_entity *entity, + caosdb_entity_value *value), { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - std::vector<bool> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_entity->SetValue(value_list); - return 0; - }) + auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity); + auto *wrapped_value = WRAPPED_VALUE_CAST(value); -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_entity_set_string_list_value(caosdb_entity_entity *entity, - const char **value, - const int length), - { - auto *wrapped_entity = - static_cast<caosdb::entity::Entity *>(entity->wrapped_entity); - std::vector<std::string> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(std::string(value[i])); - } - wrapped_entity->SetValue(value_list); - return 0; + return wrapped_entity->SetValue(*wrapped_value); }) ERROR_RETURN_CODE(GENERIC_ERROR, @@ -1180,29 +1185,23 @@ CAOSDB_PARENT_SET(name, name, wrapped_parent->SetName(std::string(name));) CAOSDB_PROPERTY_SET(name, name, wrapped_property->SetName(std::string(name));) CAOSDB_PROPERTY_SET(id, id, wrapped_property->SetId(std::string(id));) - ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_property_set_datatype(caosdb_entity_property *property, - const char *datatype, const bool is_ref, - const bool is_list), + caosdb_entity_datatype *datatype), { auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); - if (is_ref) { - // Refernce datatype with name of reference - wrapped_property->SetDataType(std::string(datatype), is_list); - return 0; - } else { - // Atomic datatype so get from enum - try { - auto enum_value = - ENUM_VALUE_FROM_NAME(std::string(datatype), AtomicDataType); - wrapped_property->SetDataType(enum_value, is_list); - return 0; - } catch (const std::out_of_range &exc) { - caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what()); - return caosdb::StatusCode::ENUM_MAPPING_ERROR; - } - } + auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype); + + return wrapped_property->SetDataType(*wrapped_datatype); + }) +ERROR_RETURN_CODE(GENERIC_ERROR, + int caosdb_entity_property_set_value(caosdb_entity_property *property, + caosdb_entity_value *value), + { + auto *wrapped_property = WRAPPED_PROPERTY_CAST(property); + auto *wrapped_value = WRAPPED_VALUE_CAST(value); + + return wrapped_property->SetValue(*wrapped_value); }) ERROR_RETURN_CODE(GENERIC_ERROR, @@ -1221,103 +1220,4 @@ ERROR_RETURN_CODE(GENERIC_ERROR, }) CAOSDB_PROPERTY_SET(unit, unit, wrapped_property->SetUnit(std::string(unit));) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_int_value(caosdb_entity_property *property, - const long value), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - wrapped_property->SetValue(value); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_double_value(caosdb_entity_property *property, - const double value), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - wrapped_property->SetValue(value); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_boolean_value(caosdb_entity_property *property, - const bool value), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - wrapped_property->SetValue(value); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_string_value(caosdb_entity_property *property, - const char *value), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - wrapped_property->SetValue(std::string(value)); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_int_list_value(caosdb_entity_property *property, - const long *value, - const int length), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - std::vector<long> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_property->SetValue(value_list); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_double_list_value(caosdb_entity_property *property, - const double *value, - const int length), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - std::vector<double> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_property->SetValue(value_list); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_boolean_list_value( - caosdb_entity_property *property, const bool *value, const int length), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - std::vector<bool> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(value[i]); - } - wrapped_property->SetValue(value_list); - return 0; - }) - -ERROR_RETURN_CODE(GENERIC_ERROR, - int caosdb_entity_property_set_string_list_value(caosdb_entity_property *property, - const char **value, - const int length), - { - auto *wrapped_property = - static_cast<caosdb::entity::Property *>(property->wrapped_property); - std::vector<std::string> value_list; - for (int i = 0; i < length; i++) { - value_list.push_back(std::string(value[i])); - } - wrapped_property->SetValue(value_list); - return 0; - }) } diff --git a/src/cxxcaosdbcli.cpp b/src/cxxcaosdbcli.cpp index 5e899953232f4be4e800150e139196e78bf70198..bb60696e8a0f3b10d2b29e54cac034b89907f9c3 100644 --- a/src/cxxcaosdbcli.cpp +++ b/src/cxxcaosdbcli.cpp @@ -26,11 +26,14 @@ #include "caosdb/entity.h" // for Entity #include "caosdb/exceptions.h" // for ConfigurationError #include "caosdb/info.h" // for VersionInfo +#include "caosdb/logging.h" // for CAOSDB_LOG_TRACE #include "caosdb/transaction.h" // for Transaction, ResultSet #include <iostream> // for operator<<, basic_ostream, basic_ost... #include <memory> // for unique_ptr, allocator, __shared_ptr_... #include <string> // for operator<<, char_traits +const auto logger_name = "libcaosdb"; + auto main() -> int { std::cout << "CaosDB C++ client (libcaosdb " << caosdb::LIBCAOSDB_VERSION_MINOR << "." @@ -51,19 +54,34 @@ auto main() -> int { // retrieve an entity auto transaction(connection->CreateTransaction()); transaction->RetrieveById("120"); - transaction->Execute(); + transaction->ExecuteAsynchronously(); + auto t_stat = transaction->WaitForIt(); + CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // " + << t_stat.GetDescription(); const auto &result_set = transaction->GetResultSet(); - - // print information - const auto &ent = result_set.at(0); - const auto &props = ent.GetProperties(); - std::cout << "Entity Name: " << ent.GetName() << std::endl; - std::cout << "Entity Description: " << ent.GetDescription() << std::endl; - std::cout << "Entity Properties: " << std::endl; - for (const auto &prop : props) { - std::cout << "----------\n" << prop.ToString() << std::endl; + if (result_set.size() > 0) { + // print information + const auto &ent = result_set.at(0); + const auto &props = ent.GetProperties(); + std::cout << "Entity Name: " << ent.GetName() << std::endl; + std::cout << "Entity Description: " << ent.GetDescription() << std::endl; + std::cout << "Entity Properties: " << std::endl; + for (const auto &prop : props) { + std::cout << "----------\n" << prop.ToString() << std::endl; + } + } else { + std::cout << "No entity \"120\" retrieved, maybe it does not exist?\n" << std::endl; } + // execute a query + std::string query("FIND Property \"Prop *\""); + std::cout << "Trying to execute a query:\n" << query << std::endl; + auto q_transaction(connection->CreateTransaction()); + q_transaction->Query(query); + q_transaction->ExecuteAsynchronously(); + t_stat = q_transaction->WaitForIt(); + CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // " + << t_stat.GetDescription(); return 0; } catch (const caosdb::exceptions::ConfigurationError &exc) { std::cout << "ConfigurationError: " << exc.what() << std::endl; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6edff54857aca64b64c99a47ea8344ebf9eee265..aefe6846d23f2cc3a145cbf6158101e7e1bb3df4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,7 @@ set(test_cases test_entity test_file_transmission test_info + test_issues test_list_properties test_protobuf test_transaction @@ -60,6 +61,7 @@ foreach (i RANGE "${len_test_cases}") target_include_directories(${test_case_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) if(_LINTING) + message(STATUS "linting for tests: ${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}") set_target_properties(${test_case_name} PROPERTIES CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_TEST_CHECKS}" diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp index fe9142862b1b4da10f23db06a2a20abe529ae35a..1059f161525d513389efd4df95cdd04ec5a088db 100644 --- a/test/test_ccaosdb.cpp +++ b/test/test_ccaosdb.cpp @@ -23,13 +23,13 @@ #include "caosdb/configuration.h" #include "caosdb/status_code.h" // for StatusCode #include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR -#include "ccaosdb.h" // for caosdb_utility_get_env_var +#include "ccaosdb.h" // for caosdb_utility_get_env_fallback +#include <cstdint> // for int64_t #include <cstring> // for strcmp #include <gtest/gtest-message.h> // for Message #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST -#include <iostream> -#include <string> // for allocator +#include <string> // for allocator, operator+, string,... class test_ccaosdb : public ::testing::Test { protected: @@ -42,8 +42,8 @@ protected: void TearDown() override { caosdb::configuration::ConfigurationManager::Clear(); } }; -TEST_F(test_ccaosdb, test_get_env_var) { - const char *const some_var = caosdb_utility_get_env_var("SOME_ENV_VAR", "fall-back"); +TEST_F(test_ccaosdb, test_get_env_fallback) { + const char *const some_var = caosdb_utility_get_env_fallback("SOME_ENV_VAR", "fall-back"); EXPECT_EQ("fall-back", some_var); } @@ -127,6 +127,258 @@ TEST_F(test_ccaosdb, test_query) { EXPECT_EQ(return_code, 0); } +TEST_F(test_ccaosdb, test_datatype) { + + caosdb_entity_datatype atomic; + // check that this fails + int return_code(caosdb_entity_create_atomic_datatype(&atomic, "some type")); + EXPECT_EQ(return_code, caosdb::StatusCode::ENUM_MAPPING_ERROR); + + return_code = caosdb_entity_create_atomic_datatype(&atomic, "INTEGER"); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype reference; + return_code = caosdb_entity_create_reference_datatype(&reference, "MyType"); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype list_of_atomics; + return_code = caosdb_entity_create_atomic_list_datatype(&list_of_atomics, "DATETIME"); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype list_of_references; + return_code = caosdb_entity_create_reference_list_datatype(&list_of_references, "MyType"); + EXPECT_EQ(return_code, 0); + + bool is_a(false); + return_code = caosdb_entity_datatype_is_undefined(&atomic, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_atomic(&atomic, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + return_code = caosdb_entity_datatype_is_reference(&atomic, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&atomic, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&atomic, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + + return_code = caosdb_entity_datatype_is_atomic(&reference, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_reference(&reference, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&reference, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&reference, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + + return_code = caosdb_entity_datatype_is_atomic(&list_of_atomics, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_reference(&list_of_atomics, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&list_of_atomics, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&list_of_atomics, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + + return_code = caosdb_entity_datatype_is_atomic(&list_of_references, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_reference(&list_of_references, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&list_of_references, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&list_of_references, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + + char *name = nullptr; // NOLINT + return_code = caosdb_entity_datatype_get_datatype_name(&atomic, &name); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(name, "INTEGER"); + + return_code = caosdb_entity_datatype_get_datatype_name(&reference, &name); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(name, "MyType"); + + return_code = caosdb_entity_datatype_get_datatype_name(&list_of_atomics, &name); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(name, "DATETIME"); + + return_code = caosdb_entity_datatype_get_datatype_name(&list_of_references, &name); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(name, "MyType"); + + return_code = caosdb_entity_delete_datatype(&atomic); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&reference); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&list_of_atomics); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&list_of_references); + EXPECT_EQ(return_code, 0); +} + +TEST_F(test_ccaosdb, test_value) { + + caosdb_entity_value string_value; + int return_code(caosdb_entity_create_string_value(&string_value, "value")); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value int_value; + return_code = caosdb_entity_create_int_value(&int_value, 27); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value bool_value; + return_code = caosdb_entity_create_bool_value(&bool_value, true); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value double_value; + return_code = caosdb_entity_create_double_value(&double_value, 2.7); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value string_vector_value; + const char *string_values[] = {"a", "b", "c"}; // NOLINT + return_code = caosdb_entity_create_string_vector_value(&string_vector_value, string_values, 3); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value int_vector_value; + const int64_t int_values[] = {1, 2, 3}; // NOLINT + return_code = caosdb_entity_create_int_vector_value(&int_vector_value, int_values, 3); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value double_vector_value; + const double double_values[] = {1.1, 2.2, 3.3}; // NOLINT + return_code = caosdb_entity_create_double_vector_value(&double_vector_value, double_values, 3); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value bool_vector_value; + const bool bool_values[] = {true, false, false}; // NOLINT + return_code = caosdb_entity_create_bool_vector_value(&bool_vector_value, bool_values, 3); + EXPECT_EQ(return_code, 0); + + // One thorough check, afterwards only the ones that should be true + bool is_a(false); + return_code = caosdb_entity_value_is_null(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_value_is_string(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + return_code = caosdb_entity_value_is_double(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_value_is_integer(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_value_is_bool(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_value_is_vector(&string_value, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + + caosdb_entity_value_is_integer(&int_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_bool(&bool_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_double(&double_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_vector(&string_vector_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_vector(&int_vector_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_vector(&double_vector_value, &is_a); + EXPECT_TRUE(is_a); + + caosdb_entity_value_is_vector(&bool_vector_value, &is_a); + EXPECT_TRUE(is_a); + + char *out_string = nullptr; // NOLINT + return_code = caosdb_entity_value_get_as_string(&string_value, &out_string); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(out_string, "value"); + + int64_t out_int(0); + return_code = caosdb_entity_value_get_as_integer(&int_value, &out_int); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(out_int, 27); + + bool out_bool(false); + return_code = caosdb_entity_value_get_as_bool(&bool_value, &out_bool); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(out_bool); + + double out_double(0); + return_code = caosdb_entity_value_get_as_double(&double_value, &out_double); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(out_double, 2.7); + + int list_length(0); + return_code = caosdb_entity_value_get_as_vector_size(&string_vector_value, &list_length); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(list_length, 3); + return_code = caosdb_entity_value_get_as_vector_size(&int_vector_value, &list_length); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(list_length, 3); + return_code = caosdb_entity_value_get_as_vector_size(&double_vector_value, &list_length); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(list_length, 3); + return_code = caosdb_entity_value_get_as_vector_size(&bool_vector_value, &list_length); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(list_length, 3); + + // Only check for one, rest should be covered by this + scalar values + caosdb_entity_value out_val; + return_code = caosdb_entity_value_get_as_vector_at(&string_vector_value, &out_val, 0); + EXPECT_EQ(return_code, 0); + caosdb_entity_value_get_as_string(&out_val, &out_string); + EXPECT_STREQ(out_string, "a"); + return_code = caosdb_entity_value_get_as_vector_at(&string_vector_value, &out_val, 1); + EXPECT_EQ(return_code, 0); + caosdb_entity_value_get_as_string(&out_val, &out_string); + EXPECT_STREQ(out_string, "b"); + return_code = caosdb_entity_value_get_as_vector_at(&string_vector_value, &out_val, 2); + EXPECT_EQ(return_code, 0); + caosdb_entity_value_get_as_string(&out_val, &out_string); + EXPECT_STREQ(out_string, "c"); + + return_code = caosdb_entity_delete_value(&string_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&int_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&bool_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&double_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&string_vector_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&int_vector_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&double_vector_value); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&bool_vector_value); + EXPECT_EQ(return_code, 0); +} + TEST_F(test_ccaosdb, test_entity) { caosdb_entity_entity entity; @@ -174,32 +426,94 @@ TEST_F(test_ccaosdb, test_entity) { caosdb_entity_entity_get_description(&entity, &out); EXPECT_EQ(strcmp(out, "The length of an object"), 0); - caosdb_entity_entity_set_datatype(&entity, "DOUBLE", false, false); - bool is_list[] = {false}; // NOLINT - bool is_ref[] = {false}; // NOLINT - caosdb_entity_entity_get_datatype(&entity, &out, is_ref, is_list); - EXPECT_EQ(strcmp(out, "DOUBLE"), 0); - EXPECT_FALSE(*is_list); - EXPECT_FALSE(*is_ref); + caosdb_entity_datatype in_type; + caosdb_entity_create_atomic_datatype(&in_type, "DOUBLE"); + caosdb_entity_entity_set_datatype(&entity, &in_type); + + // verify that this doesn't work ... + return_code = caosdb_entity_entity_get_datatype(&entity, &in_type); + EXPECT_EQ(return_code, caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR); + caosdb_entity_datatype out_type; + // ... but does with a clean property + return_code = caosdb_entity_entity_get_datatype(&entity, &out_type); + EXPECT_EQ(return_code, 0); + bool is_a(false); + return_code = caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + return_code = caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "DOUBLE"); + + caosdb_entity_value in_value; + return_code = caosdb_entity_create_double_value(&in_value, 5.0); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_entity_set_value(&entity, &in_value); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value out_value; + return_code = caosdb_entity_entity_get_value(&entity, &out_value); + EXPECT_EQ(return_code, 0); + + caosdb_entity_value_is_double(&out_value, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_value_is_null(&out_value, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_value_is_string(&out_value, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_value_is_bool(&out_value, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_value_is_integer(&out_value, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_value_is_vector(&out_value, &is_a); + EXPECT_FALSE(is_a); - caosdb_entity_entity_set_datatype(&entity, "Person", true, true); - caosdb_entity_entity_get_datatype(&entity, &out, is_ref, is_list); - EXPECT_EQ(strcmp(out, "Person"), 0); - EXPECT_TRUE(*is_list); - EXPECT_TRUE(*is_ref); + double out_double(0); + caosdb_entity_value_get_as_double(&out_value, &out_double); + EXPECT_EQ(out_double, 5.0); + + // clear to re-use + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + caosdb_entity_create_reference_list_datatype(&in_type, "Person"); + caosdb_entity_entity_set_datatype(&entity, &in_type); + + // works without clearing since datatype is managed by the owning entity + caosdb_entity_entity_get_datatype(&entity, &out_type); + return_code = caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_FALSE(is_a); + return_code = caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_EQ(return_code, 0); + EXPECT_TRUE(is_a); + + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "Person"); caosdb_entity_entity_set_unit(&entity, "m"); caosdb_entity_entity_get_unit(&entity, &out); EXPECT_EQ(strcmp(out, "m"), 0); - return_code = caosdb_entity_entity_set_double_value(&entity, 5.0); + return_code = caosdb_entity_delete_entity(&entity); EXPECT_EQ(return_code, 0); - double value[] = {0.0}; // NOLINT - return_code = caosdb_entity_entity_get_double_value(&entity, value); + return_code = caosdb_entity_delete_datatype(&in_type); EXPECT_EQ(return_code, 0); - EXPECT_EQ(*value, 5.0); - - return_code = caosdb_entity_delete_entity(&entity); + return_code = caosdb_entity_delete_value(&in_value); EXPECT_EQ(return_code, 0); } @@ -232,10 +546,14 @@ TEST_F(test_ccaosdb, test_property) { caosdb_entity_property_set_id(&property, "some_id"); caosdb_entity_property_set_name(&property, "some_name"); - caosdb_entity_property_set_datatype(&property, "TEXT", false, false); + caosdb_entity_datatype in_type; + caosdb_entity_create_atomic_datatype(&in_type, "TEXT"); + caosdb_entity_property_set_datatype(&property, &in_type); caosdb_entity_property_set_importance(&property, "FIX"); caosdb_entity_property_set_unit(&property, "some_unit"); - caosdb_entity_property_set_string_value(&property, "some_value"); + caosdb_entity_value in_value; + caosdb_entity_create_string_value(&in_value, "some_value"); + caosdb_entity_property_set_value(&property, &in_value); char *out = nullptr; // NOLINT caosdb_entity_property_get_id(&property, &out); @@ -244,12 +562,22 @@ TEST_F(test_ccaosdb, test_property) { caosdb_entity_property_get_name(&property, &out); EXPECT_EQ(strcmp(out, "some_name"), 0); - bool is_ref[] = {false}; // NOLINT - bool is_list[] = {false}; // NOLINT - caosdb_entity_property_get_datatype(&property, &out, is_ref, is_list); - EXPECT_EQ(strcmp(out, "TEXT"), 0); - EXPECT_FALSE(*is_ref); - EXPECT_FALSE(*is_list); + caosdb_entity_datatype out_type; + return_code = caosdb_entity_property_get_datatype(&property, &out_type); + EXPECT_EQ(return_code, 0); + bool is_a(false); + + caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "TEXT"); caosdb_entity_property_get_importance(&property, &out); EXPECT_EQ(strcmp(out, "FIX"), 0); @@ -257,48 +585,201 @@ TEST_F(test_ccaosdb, test_property) { caosdb_entity_property_get_unit(&property, &out); EXPECT_EQ(strcmp(out, "some_unit"), 0); - caosdb_entity_property_get_string_value(&property, &out); - EXPECT_EQ(strcmp(out, "some_value"), 0); + caosdb_entity_value out_value; + return_code = caosdb_entity_property_get_value(&property, &out_value); + EXPECT_EQ(return_code, 0); + caosdb_entity_value_is_string(&out_value, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_value_get_as_string(&out_value, &out); + EXPECT_STREQ(out, "some_value"); return_code = caosdb_entity_delete_property(&property); EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&in_value); + EXPECT_EQ(return_code, 0); } -TEST_F(test_ccaosdb, test_list_property) { +TEST_F(test_ccaosdb, test_string_list_property) { caosdb_entity_property property; int return_code(caosdb_entity_create_property(&property)); EXPECT_EQ(return_code, 0); - return_code = caosdb_entity_property_set_datatype(&property, "TEXT", false, true); + caosdb_entity_datatype in_type; + return_code = caosdb_entity_create_atomic_list_datatype(&in_type, "TEXT"); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_property_set_datatype(&property, &in_type); EXPECT_EQ(return_code, 0); + caosdb_entity_value in_value; const char *value_list[] = {"val0", "val1", "val2"}; // NOLINT - return_code = caosdb_entity_property_set_string_list_value(&property, value_list, 3); + return_code = caosdb_entity_create_string_vector_value(&in_value, value_list, 3); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_property_set_value(&property, &in_value); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype out_type; + return_code = caosdb_entity_property_get_datatype(&property, &out_type); + EXPECT_EQ(return_code, 0); + + bool is_a(false); + caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + char *out = nullptr; // NOLINT + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "TEXT"); + + caosdb_entity_value out_value; + caosdb_entity_property_get_value(&property, &out_value); + caosdb_entity_value_is_vector(&out_value, &is_a); + EXPECT_TRUE(is_a); + int length(-1); + caosdb_entity_value_get_as_vector_size(&out_value, &length); + EXPECT_EQ(length, 3); + + caosdb_entity_value list_elt; + for (int i = 0; i < length; i++) { + return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_value_get_as_string(&list_elt, &out); + EXPECT_EQ(return_code, 0); + EXPECT_STREQ(value_list[i], out); // NOLINT + } + + return_code = caosdb_entity_delete_property(&property); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&in_value); + EXPECT_EQ(return_code, 0); +} + +TEST_F(test_ccaosdb, test_int_list_property) { + + caosdb_entity_property property; + int return_code(caosdb_entity_create_property(&property)); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype in_type; + caosdb_entity_create_atomic_list_datatype(&in_type, "INTEGER"); + return_code = caosdb_entity_property_set_datatype(&property, &in_type); EXPECT_EQ(return_code, 0); - char *out = nullptr; // NOLINT - bool is_ref[] = {false}; // NOLINT - bool is_list[] = {false}; // NOLINT - return_code = caosdb_entity_property_get_datatype(&property, &out, is_ref, is_list); + const int64_t value_list[] = {1, 2, 3}; // NOLINT + caosdb_entity_value in_value; + caosdb_entity_create_int_vector_value(&in_value, value_list, 3); + return_code = caosdb_entity_property_set_value(&property, &in_value); EXPECT_EQ(return_code, 0); - EXPECT_EQ(strcmp(out, "TEXT"), 0); - EXPECT_FALSE(*is_ref); - EXPECT_TRUE(*is_list); - int length[] = {0}; // NOLINT - return_code = caosdb_entity_property_get_value_list_length(&property, length); + caosdb_entity_datatype out_type; + return_code = caosdb_entity_property_get_datatype(&property, &out_type); EXPECT_EQ(return_code, 0); - EXPECT_EQ(*length, 3); - for (int i = 0; i < *length; i++) { - return_code = caosdb_entity_property_get_string_list_value_at(&property, &out, i); + bool is_a(false); + caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + char *out = nullptr; // NOLINT + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "INTEGER"); + + caosdb_entity_value out_value; + caosdb_entity_property_get_value(&property, &out_value); + caosdb_entity_value_is_vector(&out_value, &is_a); + EXPECT_TRUE(is_a); + int length(-1); + caosdb_entity_value_get_as_vector_size(&out_value, &length); + EXPECT_EQ(length, 3); + + int64_t out_int = -1; + caosdb_entity_value list_elt; + for (int i = 0; i < length; i++) { + return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_value_get_as_integer(&list_elt, &out_int); EXPECT_EQ(return_code, 0); - EXPECT_EQ(strcmp(value_list[i], out), 0); // NOLINT + EXPECT_EQ(value_list[i], out_int); // NOLINT } return_code = caosdb_entity_delete_property(&property); EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&in_value); + EXPECT_EQ(return_code, 0); +} + +TEST_F(test_ccaosdb, test_bool_list_property) { + + caosdb_entity_property property; + int return_code(caosdb_entity_create_property(&property)); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype in_type; + caosdb_entity_create_atomic_list_datatype(&in_type, "BOOLEAN"); + return_code = caosdb_entity_property_set_datatype(&property, &in_type); + EXPECT_EQ(return_code, 0); + + const bool value_list[] = {true, true, false}; // NOLINT + caosdb_entity_value in_value; + caosdb_entity_create_bool_vector_value(&in_value, value_list, 3); + return_code = caosdb_entity_property_set_value(&property, &in_value); + EXPECT_EQ(return_code, 0); + + caosdb_entity_datatype out_type; + return_code = caosdb_entity_property_get_datatype(&property, &out_type); + EXPECT_EQ(return_code, 0); + + bool is_a(false); + caosdb_entity_datatype_is_atomic(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a); + EXPECT_TRUE(is_a); + caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a); + EXPECT_FALSE(is_a); + char *out = nullptr; // NOLINT + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(out, "BOOLEAN"); + + caosdb_entity_value out_value; + caosdb_entity_property_get_value(&property, &out_value); + caosdb_entity_value_is_vector(&out_value, &is_a); + EXPECT_TRUE(is_a); + int length(-1); + caosdb_entity_value_get_as_vector_size(&out_value, &length); + EXPECT_EQ(length, 3); + + bool out_bool(false); + caosdb_entity_value list_elt; + for (int i = 0; i < length; i++) { + return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_value_get_as_bool(&list_elt, &out_bool); + EXPECT_EQ(return_code, 0); + EXPECT_EQ(value_list[i], out_bool); // NOLINT + } + + return_code = caosdb_entity_delete_property(&property); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&in_value); + EXPECT_EQ(return_code, 0); } TEST_F(test_ccaosdb, test_entity_with_parent_and_property) { @@ -316,8 +797,12 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) { caosdb_entity_property_set_id(&input_property, "property_id"); caosdb_entity_property_set_name(&input_property, "property_name"); - caosdb_entity_property_set_datatype(&input_property, "TEXT", false, false); - caosdb_entity_property_set_string_value(&input_property, "property_value"); + caosdb_entity_datatype in_type; + caosdb_entity_create_atomic_datatype(&in_type, "TEXT"); + caosdb_entity_value in_value; + caosdb_entity_create_string_value(&in_value, "property_value"); + caosdb_entity_property_set_datatype(&input_property, &in_type); + caosdb_entity_property_set_value(&input_property, &in_value); caosdb_entity_entity entity; return_code = caosdb_entity_create_entity(&entity); @@ -356,19 +841,37 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) { caosdb_entity_property_get_name(&output_property, &out); EXPECT_EQ(strcmp(in, out), 0); - bool is_list[] = {false}; // NOLINT - bool is_ref[] = {false}; // NOLINT - caosdb_entity_property_get_datatype(&input_property, &in, is_ref, is_list); - EXPECT_FALSE(*is_list); - EXPECT_FALSE(*is_ref); - caosdb_entity_property_get_datatype(&output_property, &out, is_ref, is_list); - EXPECT_FALSE(*is_list); - EXPECT_FALSE(*is_ref); - EXPECT_EQ(strcmp(in, out), 0); - - caosdb_entity_property_get_string_value(&input_property, &in); - caosdb_entity_property_get_string_value(&output_property, &out); - EXPECT_EQ(strcmp(in, out), 0); + caosdb_entity_datatype out_type; + caosdb_entity_property_get_datatype(&output_property, &out_type); + + bool in_is(false); + bool out_is(false); + caosdb_entity_datatype_is_atomic(&in_type, &in_is); + caosdb_entity_datatype_is_atomic(&out_type, &out_is); + EXPECT_EQ(in_is, out_is); + caosdb_entity_datatype_is_reference(&in_type, &in_is); + caosdb_entity_datatype_is_reference(&out_type, &out_is); + EXPECT_EQ(in_is, out_is); + caosdb_entity_datatype_is_list_of_atomic(&in_type, &in_is); + caosdb_entity_datatype_is_list_of_atomic(&out_type, &out_is); + EXPECT_EQ(in_is, out_is); + caosdb_entity_datatype_is_list_of_reference(&in_type, &in_is); + caosdb_entity_datatype_is_list_of_reference(&out_type, &out_is); + EXPECT_EQ(in_is, out_is); + + caosdb_entity_datatype_get_datatype_name(&in_type, &in); + caosdb_entity_datatype_get_datatype_name(&out_type, &out); + EXPECT_STREQ(in, out); + + caosdb_entity_value out_value; + caosdb_entity_property_get_value(&output_property, &out_value); + caosdb_entity_value_is_string(&in_value, &in_is); + EXPECT_TRUE(in_is); + caosdb_entity_value_is_string(&out_value, &out_is); + EXPECT_TRUE(out_is); + caosdb_entity_value_get_as_string(&in_value, &in); + caosdb_entity_value_get_as_string(&out_value, &out); + EXPECT_STREQ(in, out); caosdb_entity_parent output_parent; return_code = caosdb_entity_entity_get_parent(&entity, &output_parent, 0); @@ -389,6 +892,10 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) { EXPECT_EQ(return_code, 0); return_code = caosdb_entity_delete_entity(&entity); EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_datatype(&in_type); + EXPECT_EQ(return_code, 0); + return_code = caosdb_entity_delete_value(&in_value); + EXPECT_EQ(return_code, 0); // This tests the `_deletable` flag. The wrapped cpp objects of // `output_parent` and `output_property` are owned by the entity, so @@ -480,8 +987,7 @@ TEST_F(test_ccaosdb, test_insert_update_delete) { caosdb_entity_entity_set_file_path(&entity, "some_name"); auto return_code = caosdb_transaction_transaction_insert_entity(&insert_transaction, &entity); - // For now, nothing further can be done here, so it should be READY - EXPECT_EQ(return_code, caosdb::StatusCode::READY); + EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON); caosdb_transaction_transaction update_transaction; caosdb_connection_connection_create_transaction(&connection, &update_transaction); diff --git a/test/test_data_type.cpp b/test/test_data_type.cpp index 9200a68356695b5bde9bbd1e2125e604ea2c078c..78e5100f3ce984ece46918df4aca9ffb8f1d2522 100644 --- a/test/test_data_type.cpp +++ b/test/test_data_type.cpp @@ -22,7 +22,9 @@ #include "caosdb/data_type.h" // for DataType, AtomicDataType #include "caosdb/entity.h" // for Entity +#include "caosdb/entity/v1alpha1/main.pb.h" // for DataType, Ato... #include "caosdb/logging.h" // for CAOSDB_LOG_DEBUG +#include "caosdb/protobuf_helper.h" // for CAOSDB_DEBUG_... #include <boost/log/core/record.hpp> // for record #include <boost/log/detail/attachable_sstream_buf.hpp> // for basic_ostring... #include <boost/log/sources/record_ostream.hpp> // for operator<< @@ -32,7 +34,9 @@ #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApi... #include <gtest/gtest_pred_impl.h> // for AssertionResult, Test #include <iosfwd> // for streamsize +#include <map> // for map, operator!= #include <string> // for allocator +#include <utility> // for pair namespace caosdb::entity { using ProtoEntity = caosdb::entity::v1alpha1::Entity; @@ -49,17 +53,17 @@ TEST(test_data_type, test_atomic) { // the different AtomicDataType are associated with integers entity.SetDataType(map_el.first); EXPECT_TRUE(entity.GetDataType().IsAtomic()); - EXPECT_EQ(entity.GetDataType().AsAtomic(), map_el.first); + EXPECT_EQ(entity.GetDataType().GetAsAtomic(), map_el.first); proto_data_type.set_atomic_data_type(static_cast<ProtoAtomicDataType>(map_el.first)); DataType data_type(&proto_data_type); entity.SetDataType(data_type); EXPECT_FALSE(data_type.IsReference()); - EXPECT_EQ(data_type.AsReference().GetName(), std::basic_string<char>("")); + EXPECT_EQ(data_type.GetAsReference().GetName(), std::basic_string<char>("")); EXPECT_FALSE(data_type.IsList()); EXPECT_TRUE(data_type.IsAtomic()); - EXPECT_EQ(data_type.AsAtomic(), map_el.first); + EXPECT_EQ(data_type.GetAsAtomic(), map_el.first); } } @@ -70,7 +74,7 @@ TEST(test_data_type, test_reference) { entity.SetRole(Role::PROPERTY); entity.SetDataType("Person"); EXPECT_TRUE(entity.GetDataType().IsReference()); - EXPECT_EQ(entity.GetDataType().AsReference().GetName(), "Person"); + EXPECT_EQ(entity.GetDataType().GetAsReference().GetName(), "Person"); proto_data_type.mutable_reference_data_type()->set_name("Person"); DataType data_type(&proto_data_type); @@ -79,7 +83,7 @@ TEST(test_data_type, test_reference) { EXPECT_TRUE(data_type.IsReference()); EXPECT_FALSE(data_type.IsList()); EXPECT_FALSE(data_type.IsAtomic()); - EXPECT_EQ(data_type.AsReference().GetName(), "Person"); + EXPECT_EQ(data_type.GetAsReference().GetName(), "Person"); } TEST(test_data_type, test_list_of_atomic) { @@ -89,7 +93,7 @@ TEST(test_data_type, test_list_of_atomic) { EXPECT_FALSE(data_type.IsReference()); EXPECT_FALSE(data_type.IsAtomic()); EXPECT_TRUE(data_type.IsList()); - const auto &list_data_type = data_type.AsList(); + const auto &list_data_type = data_type.GetAsList(); EXPECT_EQ(list_data_type.GetReferenceDataType().GetName(), std::basic_string<char>("")); EXPECT_TRUE(list_data_type.IsListOfAtomic()); EXPECT_FALSE(list_data_type.IsListOfReference()); @@ -104,7 +108,7 @@ TEST(test_data_type, test_list_of_reference) { EXPECT_FALSE(data_type.IsAtomic()); EXPECT_TRUE(data_type.IsList()); - const auto &list_data_type = data_type.AsList(); + const auto &list_data_type = data_type.GetAsList(); EXPECT_TRUE(list_data_type.IsListOfReference()); EXPECT_FALSE(list_data_type.IsListOfAtomic()); const auto *wrapped = list_data_type.GetReferenceDataType().GetWrapped(); @@ -113,4 +117,16 @@ TEST(test_data_type, test_list_of_reference) { EXPECT_EQ(list_data_type.GetReferenceDataType().GetName(), "person"); } +TEST(test_data_type, test_data_type_to_string) { + DataType data_type1; + EXPECT_EQ(data_type1.ToString(), "{}\n"); + + ProtoDataType proto_data_type; + DataType data_type2(&proto_data_type); + EXPECT_EQ(data_type2.ToString(), "{}\n"); + + DataType data_type3(AtomicDataType::INTEGER); + EXPECT_EQ(data_type3.ToString(), "{\n \"atomicDataType\": \"ATOMIC_DATA_TYPE_INTEGER\"\n}\n"); +} + } // namespace caosdb::entity diff --git a/test/test_entity.cpp b/test/test_entity.cpp index 95c451d8d83154352731f1947d3972586e5b6a79..de0206f8b547884b7ae438b3bd4873230b9128d8 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -20,25 +20,25 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ -#include "caosdb_test_utility.h" #include "caosdb/data_type.h" // for DataType, AtomicDat... -#include "caosdb/entity.h" // for Entity, Parent, Par... +#include "caosdb/entity.h" // for Entity, Property #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe... #include "caosdb/entity/v1alpha1/main.pb.h" // for IdResponse, Message -#include "caosdb/message_code.h" // for MessageCode +#include "caosdb/message_code.h" // for MessageCode, ENTITY... #include "caosdb/protobuf_helper.h" // for get_arena #include "caosdb/status_code.h" // for StatusCode, FILE_DO... #include "caosdb/transaction.h" // for Transaction #include "caosdb/value.h" // for Value -#include <exception> -#include <google/protobuf/arena.h> // for Arena -#include <gtest/gtest-message.h> // for Message -#include <gtest/gtest-test-part.h> // for TestPartResult, Sui... -#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ -#include <iostream> -#include <memory> // for allocator, shared_ptr -#include <stdexcept> -#include <string> // for operator+, string +#include "caosdb_test_utility.h" // for TEST_DATA_DIR +#include <google/protobuf/arena.h> // for Arena +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult, Sui... +#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ +#include <iostream> // for operator<<, basic_o... +#include <memory> // for allocator, shared_ptr +#include <stdexcept> // for out_of_range +#include <string> // for operator+, to_string +#include <utility> // for move namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; @@ -81,15 +81,15 @@ TEST(test_entity, test_property_setters) { EXPECT_EQ(prop.GetId(), "prop_id"); EXPECT_EQ(prop.GetImportance(), Importance::OBLIGATORY); EXPECT_TRUE(prop.GetValue().IsString()); - EXPECT_EQ(prop.GetValue().AsString(), "prop_value"); + EXPECT_EQ(prop.GetValue().GetAsString(), "prop_value"); EXPECT_EQ(prop.GetUnit(), "prop_unit"); EXPECT_TRUE(prop.GetDataType().IsReference()); - EXPECT_EQ(prop.GetDataType().AsReference().GetName(), "prop_dtype"); + EXPECT_EQ(prop.GetDataType().GetAsReference().GetName(), "prop_dtype"); EXPECT_FALSE(prop.GetDataType().IsList()); prop.SetDataType(AtomicDataType::DATETIME); EXPECT_TRUE(prop.GetDataType().IsAtomic()); - EXPECT_EQ(prop.GetDataType().AsAtomic(), AtomicDataType::DATETIME); + EXPECT_EQ(prop.GetDataType().GetAsAtomic(), AtomicDataType::DATETIME); EXPECT_FALSE(prop.GetDataType().IsList()); } @@ -98,16 +98,16 @@ TEST(test_entity, test_list_property_setters) { prop.SetDataType(AtomicDataType::DATETIME); // Set as atomic first. EXPECT_TRUE(prop.GetDataType().IsAtomic()); - EXPECT_EQ(prop.GetDataType().AsAtomic(), AtomicDataType::DATETIME); + EXPECT_EQ(prop.GetDataType().GetAsAtomic(), AtomicDataType::DATETIME); prop.SetDataType(AtomicDataType::DOUBLE, true); auto const &dtype = prop.GetDataType(); EXPECT_FALSE(dtype.IsAtomic()); // Should not be true anymore. EXPECT_FALSE(dtype.IsReference()); EXPECT_TRUE(dtype.IsList()); - EXPECT_NE(dtype.AsAtomic(), AtomicDataType::DATETIME); // Should be overwritten. - EXPECT_TRUE(dtype.AsList().IsListOfAtomic()); - EXPECT_EQ(dtype.AsList().GetAtomicDataType(), AtomicDataType::DOUBLE); + EXPECT_NE(dtype.GetAsAtomic(), AtomicDataType::DATETIME); // Should be overwritten. + EXPECT_TRUE(dtype.GetAsList().IsListOfAtomic()); + EXPECT_EQ(dtype.GetAsList().GetAtomicDataType(), AtomicDataType::DOUBLE); } TEST(test_entity, test_append_property) { @@ -133,11 +133,88 @@ TEST(test_entity, test_append_property) { EXPECT_EQ(prop.GetName(), same_prop.GetName()); EXPECT_EQ(prop.GetId(), same_prop.GetId()); EXPECT_EQ(prop.GetImportance(), same_prop.GetImportance()); - EXPECT_EQ(prop.GetValue(), same_prop.GetValue()); + EXPECT_EQ(prop.GetValue().ToString(), same_prop.GetValue().ToString()); EXPECT_EQ(prop.GetUnit(), same_prop.GetUnit()); EXPECT_EQ(prop.GetDataType(), same_prop.GetDataType()); } +TEST(test_entity, test_property_copy_constructor) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + Property other_prop(prop); + + EXPECT_EQ(prop, other_prop); + EXPECT_EQ(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetId(), other_prop.GetId()); + EXPECT_EQ(prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(prop.GetDataType(), other_prop.GetDataType()); +} + +TEST(test_entity, test_property_copy_assignment) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + auto other_prop = prop; + EXPECT_EQ(prop, other_prop); + EXPECT_EQ(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetId(), other_prop.GetId()); + EXPECT_EQ(prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(prop.GetDataType(), other_prop.GetDataType()); + + other_prop.SetName("other_prop_name"); + EXPECT_NE(prop, other_prop); + EXPECT_NE(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetName(), "prop_name"); + EXPECT_EQ(other_prop.GetName(), "other_prop_name"); +} + +TEST(test_entity, test_property_move_assignment) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + // we compare the moved one with this one + const Property copy_prop(prop); + + Property other_prop = std::move(prop); + // EXPECT_NE(prop, copy_prop); NOLINT + // EXPECT_NE(prop, other_prop); NOLINT + // EXPECT_EQ(prop.ToString(), "{}"); NOLINT + + EXPECT_EQ(copy_prop, other_prop); + EXPECT_EQ(copy_prop.GetName(), other_prop.GetName()); + EXPECT_EQ(copy_prop.GetId(), other_prop.GetId()); + EXPECT_EQ(copy_prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(copy_prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(copy_prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(copy_prop.GetDataType(), other_prop.GetDataType()); + + other_prop.SetName("other_prop_name"); + EXPECT_NE(copy_prop, other_prop); + EXPECT_NE(copy_prop.GetName(), other_prop.GetName()); + EXPECT_EQ(copy_prop.GetName(), "prop_name"); + EXPECT_EQ(other_prop.GetName(), "other_prop_name"); +} + TEST(test_entity, test_copy_to) { auto entity = Entity(); entity.SetRole(Role::RECORD); @@ -201,11 +278,11 @@ TEST(test_entity, test_insert_with_role) { EXPECT_EQ(entity.GetRole(), Role::PROPERTY); EXPECT_TRUE(entity.GetDataType().IsAtomic()); - EXPECT_EQ(entity.GetDataType().AsAtomic(), AtomicDataType::DOUBLE); + EXPECT_EQ(entity.GetDataType().GetAsAtomic(), AtomicDataType::DOUBLE); EXPECT_EQ(entity.GetName(), "Length"); EXPECT_EQ(entity.GetUnit(), "m"); EXPECT_TRUE(entity.GetValue().IsDouble()); - EXPECT_DOUBLE_EQ(entity.GetValue().AsDouble(), 5.5); + EXPECT_DOUBLE_EQ(entity.GetValue().GetAsDouble(), 5.5); } TEST(test_entity, test_insert_with_parent) { @@ -364,12 +441,39 @@ TEST(test_entity, test_remove_property) { // P0,P1,P2, P4,P5,P6,P7,P8,P9 entity.RemoveProperty(3); + ASSERT_EQ(entity.GetProperties().size(), 9); + for (int i = 0; i < 3; i++) { + auto name = "PROPERTY-" + std::to_string(i); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 3; i < 9; i++) { + auto name = "PROPERTY-" + std::to_string(i + 1); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + // Remove at index 6 // P0,P1,P2, P4,P5,P6,P7,P8,P9 // ^ // P0,P1,P2, P4,P5,P6, P8,P9 entity.RemoveProperty(6); ASSERT_EQ(entity.GetProperties().size(), 8); + for (int i = 0; i < 3; i++) { + auto name = "PROPERTY-" + std::to_string(i); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 3; i < 6; i++) { + auto name = "PROPERTY-" + std::to_string(i + 1); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 6; i < 8; i++) { + auto name = "PROPERTY-" + std::to_string(i + 2); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } // AppendProperty another property // P0,P1,P2, P4,P5,P6, P8,P9 @@ -461,4 +565,91 @@ TEST(test_entity, test_add_file) { EXPECT_EQ(entity.SetLocalPath(TEST_DATA_DIR + "/test.json"), StatusCode::SUCCESS); } +TEST(test_entity, test_entity_to_string) { + Entity entity; + EXPECT_EQ(entity.ToString(), "{}\n"); + + entity.SetRole(Role::PROPERTY); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); + + entity.SetValue(Value()); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); + + entity.SetDataType(DataType()); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); +} + +TEST(test_entity, test_properties_to_string) { + Entity entity; + EXPECT_EQ(entity.GetProperties().ToString(), "[]\n"); + + Property property; + property.SetName("Prop1"); + entity.AppendProperty(property); + EXPECT_EQ(entity.GetProperties().ToString(), "[\n{\n \"name\": \"Prop1\"\n}\n]\n"); + + Property property2; + property2.SetName("Prop2"); + entity.AppendProperty(property2); + EXPECT_EQ(entity.GetProperties().ToString(), + "[\n{\n \"name\": \"Prop1\"\n},\n{\n \"name\": \"Prop2\"\n}\n]\n"); +} + +TEST(test_entity, test_property_to_string) { + Property property; + EXPECT_EQ(property.ToString(), "{}\n"); + + property.SetDataType(AtomicDataType::DOUBLE); + EXPECT_EQ(property.ToString(), + "{\n \"dataType\": {\n \"atomicDataType\": \"ATOMIC_DATA_TYPE_DOUBLE\"\n }\n}\n"); +} + +TEST(test_entity, test_parents_to_string) { + Parent parent; + parent.SetName("the name"); + + Entity entity; + entity.AppendParent(parent); + + EXPECT_EQ(entity.GetParents().ToString(), "[\n{\n \"name\": \"the name\"\n}\n]\n"); +} + +TEST(test_entity, test_parent_to_string) { + Parent parent; + EXPECT_EQ(parent.ToString(), "{}\n"); + + parent.SetName("the name"); + EXPECT_EQ(parent.ToString(), "{\n \"name\": \"the name\"\n}\n"); +} + +TEST(test_entity, test_messages_to_string) { + IdResponse idResponse; + idResponse.set_id("entity_id"); + auto *error = idResponse.add_errors(); + error->set_code(MessageCode::ENTITY_DOES_NOT_EXIST); + error->set_description("error_desc"); + + Entity entity(&idResponse); + + // Messages are not printed, currently. + EXPECT_EQ(entity.ToString(), "{\n \"id\": \"entity_id\",\n \"version\": {}\n}\n"); + EXPECT_EQ(entity.GetErrors().ToString(), + "[\n{\n \"code\": 2,\n \"description\": \"error_desc\"\n}\n]\n"); +} + +TEST(test_entity, test_message_to_string) { + IdResponse idResponse; + idResponse.set_id("entity_id"); + auto *error = idResponse.add_errors(); + error->set_code(MessageCode::ENTITY_DOES_NOT_EXIST); + error->set_description("error_desc"); + + Entity entity(&idResponse); + + // Messages are not printed, currently. + EXPECT_EQ(entity.ToString(), "{\n \"id\": \"entity_id\",\n \"version\": {}\n}\n"); + EXPECT_EQ(entity.GetErrors().at(0).ToString(), + "{\n \"code\": 2,\n \"description\": \"error_desc\"\n}\n"); +} + } // namespace caosdb::entity diff --git a/test/test_issues.cpp b/test/test_issues.cpp new file mode 100644 index 0000000000000000000000000000000000000000..389c8ee40c12a89a347a811cca5a4e7dd756068e --- /dev/null +++ b/test/test_issues.cpp @@ -0,0 +1,47 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "caosdb/configuration.h" // for InsecureConnectionConfig... +#include "caosdb/connection.h" // for Connection +#include "caosdb/status_code.h" // for StatusCode, EXECUTING +#include "caosdb/transaction.h" // for Transaction +#include "caosdb/transaction_status.h" // for StatusCode +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestPa... +#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, TEST +#include <memory> // for allocator, unique_ptr + +namespace caosdb::transaction { +using caosdb::configuration::InsecureConnectionConfiguration; +using caosdb::connection::Connection; + +TEST(test_issues, test_issue_11) { + const auto *host = "localhost"; + auto configuration = InsecureConnectionConfiguration(host, 8000); + Connection connection(configuration); + auto transaction = connection.CreateTransaction(); + + ASSERT_EQ(transaction->GetResultSet().size(), 0); + transaction->RetrieveById("100"); + ASSERT_EQ(StatusCode::EXECUTING, transaction->ExecuteAsynchronously()); + // Trying to obtain ResultSet while it is still empty. + ASSERT_EQ(transaction->GetResultSet().size(), 0); +} + +} // namespace caosdb::transaction diff --git a/test/test_list_properties.cpp b/test/test_list_properties.cpp index 34895dfa15eabc207c2a485f4a22fcb33428a791..bc82896f5e7623643cb249979f381f3a7097d40d 100644 --- a/test/test_list_properties.cpp +++ b/test/test_list_properties.cpp @@ -24,6 +24,7 @@ #include "caosdb/entity.h" // for Entity #include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType #include "caosdb/value.h" // for Value +#include <cstdint> // for int64_t #include <gtest/gtest-message.h> // for Message #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApi... #include <gtest/gtest_pred_impl.h> // for AssertionResult, Test @@ -52,13 +53,48 @@ TEST(test_list_property, test_list_of_text) { const auto &value = entity.GetProperties().at(0).GetValue(); EXPECT_TRUE(data_type.IsList()); - EXPECT_TRUE(data_type.AsList().IsListOfAtomic()); - EXPECT_EQ(data_type.AsList().GetAtomicDataType(), AtomicDataType::TEXT); + EXPECT_TRUE(data_type.GetAsList().IsListOfAtomic()); + EXPECT_EQ(data_type.GetAsList().GetAtomicDataType(), AtomicDataType::TEXT); - EXPECT_TRUE(value.IsList()); - EXPECT_EQ(value.AsList().size(), 3); - EXPECT_TRUE(value.AsList().at(1).IsString()); - EXPECT_EQ(value.AsList().at(1).AsString(), "item2"); + EXPECT_TRUE(value.IsVector()); + EXPECT_EQ(value.GetAsVector().size(), 3); + EXPECT_TRUE(value.GetAsVector().at(1).IsString()); + EXPECT_EQ(value.GetAsVector().at(1).GetAsString(), "item2"); +} + +TEST(test_list_property, test_list_reassignment) { + Property list_property; + // assign int list + std::vector<int64_t> int_values{1, 2, 3}; + list_property.SetValue(int_values); + const auto &value_ints = list_property.GetValue(); + EXPECT_TRUE(value_ints.IsVector()); + EXPECT_EQ(value_ints.GetAsVector().size(), 3); + for (int ii = 0; ii < 3; ii++) { + EXPECT_TRUE(value_ints.GetAsVector().at(ii).IsInt64()); + EXPECT_EQ(value_ints.GetAsVector().at(ii).GetAsInt64(), int_values[ii]); + } + + // Re-assign to double scalar + double double_value(1.23); + list_property.SetValue(double_value); + const auto &value_double = list_property.GetValue(); + EXPECT_FALSE(value_double.IsVector()); + EXPECT_TRUE(value_double.IsDouble()); + EXPECT_FALSE(value_double.IsInt64()); + EXPECT_EQ(value_double.GetAsDouble(), double_value); + + // Re-assign to boolean list + std::vector<bool> bool_values{true, false, false, true}; + list_property.SetValue(bool_values); + const auto &value_bools = list_property.GetValue(); + EXPECT_TRUE(value_bools.IsVector()); + EXPECT_EQ(value_bools.GetAsVector().size(), 4); + for (int jj = 0; jj < 4; jj++) { + EXPECT_TRUE(value_bools.GetAsVector().at(jj).IsBool()); + EXPECT_FALSE(value_bools.GetAsVector().at(jj).IsInt64()); + EXPECT_EQ(value_bools.GetAsVector().at(jj).GetAsBool(), bool_values[jj]); + } } } // namespace caosdb::entity diff --git a/test/test_protobuf.cpp b/test/test_protobuf.cpp index 43ab6883b42f1dca1c2785103f6a274eb2b2f1bc..6f9bda0740487db475f1e34757e4a612c59061f9 100644 --- a/test/test_protobuf.cpp +++ b/test/test_protobuf.cpp @@ -102,10 +102,10 @@ TEST(test_protobuf, test_copy_nested) { EXPECT_EQ(entity_destination.data_type().reference_data_type().name(), "src_per"); Entity entity(&entity_destination); - EXPECT_EQ(entity.GetDataType().AsReference().GetName(), "src_per"); + EXPECT_EQ(entity.GetDataType().GetAsReference().GetName(), "src_per"); const Entity ©_entity(entity); - EXPECT_EQ(copy_entity.GetDataType().AsReference().GetName(), "src_per"); + EXPECT_EQ(copy_entity.GetDataType().GetAsReference().GetName(), "src_per"); } } // namespace caosdb diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index db2d76a845f5548eb68570d183e1a7867a91fe15..7c93ac5d9b081ac4cb2314f34272f54cba0f61c9 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -224,4 +224,26 @@ TEST(test_transaction, test_insert_with_file) { EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::FILE_UPLOAD_ERROR); } +TEST(test_transaction, test_copy_result_set) { + std::vector<std::unique_ptr<Entity>> entities; + for (int i = 0; i < 5; i++) { + entities.push_back(std::make_unique<Entity>()); + entities[i]->SetName("E" + std::to_string(i)); + } + MultiResultSet result_set(std::move(entities)); + MultiResultSet copy(result_set); + + EXPECT_EQ(result_set.size(), 5); + EXPECT_EQ(copy.size(), 5); + + result_set.clear(); + + EXPECT_EQ(result_set.size(), 0); + EXPECT_EQ(copy.size(), 5); + + for (int i = 0; i < 5; i++) { + EXPECT_EQ(copy.at(i).GetName(), "E" + std::to_string(i)); + } +} + } // namespace caosdb::transaction diff --git a/test/test_utility.cpp b/test/test_utility.cpp index a64fcee8c94fa527692bbd96ca0b54f94bd6f524..3a9a420cee742c1001c727a6154452ee10e8d8fb 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -26,12 +26,16 @@ #include "boost/json/value.hpp" // for value #include "caosdb/data_type.h" // for atomicdatatype_names #include "caosdb/entity.h" // for importance_names, role... +#include "caosdb/status_code.h" // for get_status_description #include "caosdb/utility.h" // for base64_encode, load_js... #include "caosdb_test_utility.h" // for TEST_DATA_DIR #include <gtest/gtest-message.h> // for Message #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteA... #include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ, TestInfo +#include <map> // for map, operator!=, _Rb_t... +#include <stdexcept> // for out_of_range #include <string> // for allocator, string, ope... +#include <utility> // for pair namespace caosdb::utility { using ::testing::ElementsAre; @@ -73,9 +77,12 @@ TEST(test_utility, enum_names) { // Some non-working examples EXPECT_THROW_MESSAGE(getEnumValueFromName<caosdb::entity::Importance>("Invalid name"), std::out_of_range, "Could not find enum value for string 'Invalid name'."); - enum e1 { a }; - EXPECT_THROW_STARTS_WITH(getEnumNameFromValue<e1>(a), std::logic_error, "Enum type "); - EXPECT_THROW_STARTS_WITH(getEnumValueFromName<e1>("Hello!"), std::logic_error, "Enum type "); +} + +TEST(test_utility, test_status_code_description) { + EXPECT_EQ(caosdb::get_status_description(12412323), "MISSING DESCRIPTION"); + EXPECT_EQ(caosdb::get_status_description(static_cast<int>(StatusCode::UNKNOWN)), + "Unknown error. This is typically a bug (server or client)."); } } // namespace caosdb::utility diff --git a/test/test_value.cpp b/test/test_value.cpp index 74d30f04b621a0e118bc7591a07aab052786c790..241f2bd0ce2a7291184a79cdaa0612f2b99fa204 100644 --- a/test/test_value.cpp +++ b/test/test_value.cpp @@ -22,6 +22,7 @@ #include "caosdb/value.h" // for Value #include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType +#include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper #include <algorithm> // for max #include <cmath> // for isnan #include <gtest/gtest-message.h> // for Message @@ -32,6 +33,7 @@ #include <vector> // for vector namespace caosdb::entity { +using ProtoValue = caosdb::entity::v1alpha1::Value; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using ProtoParent = caosdb::entity::v1alpha1::Parent; using ProtoDataType = caosdb::entity::v1alpha1::DataType; @@ -42,9 +44,9 @@ TEST(test_value, test_null) { EXPECT_TRUE(value.IsNull()); EXPECT_FALSE(value.IsDouble()); EXPECT_FALSE(value.IsBool()); - EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsInt64()); EXPECT_FALSE(value.IsString()); - EXPECT_FALSE(value.IsList()); + EXPECT_FALSE(value.IsVector()); } TEST(test_value, test_string) { @@ -53,18 +55,18 @@ TEST(test_value, test_string) { EXPECT_TRUE(value.IsString()); EXPECT_FALSE(value.IsDouble()); EXPECT_FALSE(value.IsBool()); - EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsInt64()); - EXPECT_EQ(value.AsString(), "test"); + EXPECT_EQ(value.GetAsString(), "test"); Value empty_string(std::string("")); EXPECT_FALSE(empty_string.IsNull()); EXPECT_TRUE(empty_string.IsString()); EXPECT_FALSE(empty_string.IsDouble()); EXPECT_FALSE(empty_string.IsBool()); - EXPECT_FALSE(empty_string.IsInteger()); + EXPECT_FALSE(empty_string.IsInt64()); - EXPECT_EQ(empty_string.AsString(), ""); + EXPECT_EQ(empty_string.GetAsString(), ""); // Test inequality Value string1("1"); @@ -78,18 +80,18 @@ TEST(test_value, test_double) { EXPECT_FALSE(value.IsString()); EXPECT_TRUE(value.IsDouble()); EXPECT_FALSE(value.IsBool()); - EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsInt64()); - EXPECT_EQ(value.AsDouble(), 2.26); + EXPECT_EQ(value.GetAsDouble(), 2.26); Value nan(std::sqrt(-1.0)); EXPECT_FALSE(nan.IsNull()); EXPECT_FALSE(nan.IsString()); EXPECT_TRUE(nan.IsDouble()); EXPECT_FALSE(nan.IsBool()); - EXPECT_FALSE(nan.IsInteger()); + EXPECT_FALSE(nan.IsInt64()); - EXPECT_TRUE(std::isnan(nan.AsDouble())); + EXPECT_TRUE(std::isnan(nan.GetAsDouble())); } TEST(test_value, test_integer) { @@ -98,9 +100,9 @@ TEST(test_value, test_integer) { EXPECT_FALSE(value.IsString()); EXPECT_FALSE(value.IsDouble()); EXPECT_FALSE(value.IsBool()); - EXPECT_TRUE(value.IsInteger()); + EXPECT_TRUE(value.IsInt64()); - EXPECT_EQ(value.AsInteger(), 1337); + EXPECT_EQ(value.GetAsInt64(), 1337); } TEST(test_value, test_boolean) { @@ -109,9 +111,9 @@ TEST(test_value, test_boolean) { EXPECT_FALSE(value.IsString()); EXPECT_FALSE(value.IsDouble()); EXPECT_TRUE(value.IsBool()); - EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsInt64()); - EXPECT_EQ(value.AsBool(), true); + EXPECT_EQ(value.GetAsBool(), true); } TEST(test_value, test_list) { @@ -122,17 +124,73 @@ TEST(test_value, test_list) { Value value(ids); EXPECT_FALSE(value.IsNull()); - EXPECT_TRUE(value.IsList()); + EXPECT_TRUE(value.IsVector()); EXPECT_FALSE(value.IsString()); EXPECT_FALSE(value.IsDouble()); EXPECT_FALSE(value.IsBool()); - EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsInt64()); - auto list_value = value.AsList(); + auto list_value = value.GetAsVector(); int counter = 0; - for (auto item : list_value) { + for (const auto &item : list_value) { EXPECT_EQ(item.IsString(), true); - EXPECT_EQ(item.AsString(), "id" + std::to_string(counter++)); + EXPECT_EQ(item.GetAsString(), "id" + std::to_string(counter++)); } } + +TEST(test_value, test_scalar_value_to_value) { + ProtoScalarValue proto_scalar_value; + proto_scalar_value.set_integer_value(5); + ScalarValue scalar_value(&proto_scalar_value); + Value value(scalar_value); + + EXPECT_TRUE(scalar_value.IsInt64()); + EXPECT_TRUE(value.IsInt64()); + EXPECT_EQ(scalar_value.GetAsInt64(), value.GetAsInt64()); +} + +TEST(test_value, test_abstract_value) { + std::vector<double> vals; + for (double num : {0.0, 5.6, 27.5}) { + vals.push_back(num); + } + Value value(vals); + EXPECT_TRUE(value.IsVector()); + + AbstractValue *abstract_value = &value; + EXPECT_TRUE(abstract_value->IsVector()); + + Value value2(*abstract_value); + EXPECT_TRUE(value2.IsVector()); + + ScalarValue scalar_value = value.GetAsVector().at(2); + EXPECT_TRUE(scalar_value.IsDouble()); + EXPECT_EQ(scalar_value.GetAsDouble(), 27.5); + + AbstractValue *abstract_scalar_value = &scalar_value; + EXPECT_TRUE(abstract_scalar_value->IsDouble()); + EXPECT_EQ(abstract_scalar_value->GetAsDouble(), 27.5); + + Value scalar_value2(*abstract_scalar_value); + EXPECT_TRUE(scalar_value2.IsDouble()); + EXPECT_EQ(scalar_value2.GetAsDouble(), 27.5); +} + +TEST(test_value, test_value_to_string) { + Value value1; + EXPECT_EQ(value1.ToString(), "{}\n"); + + ProtoValue proto_value; + Value value2(&proto_value); + EXPECT_EQ(value2.ToString(), "{}\n"); + + Value value3(2.6); + EXPECT_EQ(value3.ToString(), "{\n \"scalarValue\": {\n \"doubleValue\": 2.6\n }\n}\n"); + + Value value4(std::vector<bool>{true, false}); + EXPECT_EQ(value4.ToString(), + "{\n \"listValues\": {\n \"values\": [\n {\n \"booleanValue\": true\n },\n " + "{\n \"booleanValue\": false\n }\n ]\n }\n}\n"); +} + } // namespace caosdb::entity