Skip to content
Snippets Groups Projects
Commit 5449a831 authored by Daniel Hornung's avatar Daniel Hornung
Browse files

FIX: Workaround: manually parse and convert non-string values.

parent 965205da
No related branches found
No related tags found
No related merge requests found
Pipeline #12496 failed
......@@ -454,7 +454,9 @@ class Property {
public:
explicit inline Property(ProtoProperty *other)
: value(Value(other->mutable_value())), data_type(DataType(other->mutable_data_type())),
wrapped(other){};
wrapped(other){
FixValue();
};
Property();
/**
......@@ -551,6 +553,13 @@ 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;
......@@ -602,6 +611,7 @@ public:
errors.wrapped = CreateMessagesField();
warnings.wrapped = CreateMessagesField();
infos.wrapped = CreateMessagesField();
FixValue();
};
explicit Entity(IdResponse *id_response);
explicit Entity(ProtoEntity *other)
......@@ -614,11 +624,13 @@ public:
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();
};
[[nodiscard]] inline auto GetId() const noexcept -> const std::string & { return wrapped->id(); };
......@@ -744,6 +756,13 @@ private:
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;
......
......@@ -20,10 +20,12 @@
*
*/
#include "caosdb/entity.h"
#include "caosdb/exceptions.h"
#include "caosdb/data_type.h" // for DataType
#include "caosdb/entity/v1alpha1/main.pb.h" // for Messages
#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
......@@ -42,6 +44,13 @@ 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.
......@@ -66,7 +75,9 @@ auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); }
return this->wrapped->description();
}
Property::Property() : Property(Property::CreateProtoProperty()) {}
Property::Property() : Property(Property::CreateProtoProperty()) {
FixValue();
}
auto Property::CreateProtoProperty() -> ProtoProperty * {
return Arena::CreateMessage<ProtoProperty>(get_arena());
......@@ -152,6 +163,11 @@ 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; }
auto Entity::AppendParent(const Parent &parent) -> void { this->parents.Append(parent); }
......@@ -174,9 +190,12 @@ Entity::Entity(IdResponse *id_response) : Entity() {
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()) {}
Entity::Entity() : Entity(Entity::CreateProtoEntity()) {
FixValue();
}
auto Entity::CreateMessagesField() -> RepeatedPtrField<ProtoMessage> * {
return Arena::CreateMessage<RepeatedPtrField<ProtoMessage>>(get_arena());
......@@ -256,4 +275,104 @@ auto Entity::SetFilePath(const std::string &path) -> void {
this->wrapped->mutable_file_descriptor()->set_path(path);
}
auto Entity::FixValue() -> void {
FixValueImpl(this);
}
// Utility functions //////////////////////////////////////////////////////////
template<typename E>
auto FixValueImpl(E* ent) -> void {
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<long> 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()) {
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) {
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment