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 &copy_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