// Copyright 2022 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #include "communication/bolt/v1/value.hpp" #include "utils/algorithm.hpp" #include "utils/string.hpp" namespace memgraph::communication::bolt { #define DEF_GETTER_BY_VAL(type, value_type, field) \ value_type &Value::Value##type() { \ if (type_ != Type::type) throw ValueException(); \ return field; \ } \ value_type Value::Value##type() const { \ if (type_ != Type::type) throw ValueException(); \ return field; \ } DEF_GETTER_BY_VAL(Bool, bool, bool_v) DEF_GETTER_BY_VAL(Int, int64_t, int_v) DEF_GETTER_BY_VAL(Double, double, double_v) #undef DEF_GETTER_BY_VAL #define DEF_GETTER_BY_REF(type, value_type, field) \ value_type &Value::Value##type() { \ if (type_ != Type::type) throw ValueException(); \ return field; \ } \ const value_type &Value::Value##type() const { \ if (type_ != Type::type) throw ValueException(); \ return field; \ } DEF_GETTER_BY_REF(String, std::string, string_v) DEF_GETTER_BY_REF(List, std::vector, list_v) using map_t = std::map; DEF_GETTER_BY_REF(Map, map_t, map_v) DEF_GETTER_BY_REF(Vertex, Vertex, vertex_v) DEF_GETTER_BY_REF(Edge, Edge, edge_v) DEF_GETTER_BY_REF(UnboundedEdge, UnboundedEdge, unbounded_edge_v) DEF_GETTER_BY_REF(Path, Path, path_v) DEF_GETTER_BY_REF(Date, utils::Date, date_v) DEF_GETTER_BY_REF(LocalTime, utils::LocalTime, local_time_v) DEF_GETTER_BY_REF(LocalDateTime, utils::LocalDateTime, local_date_time_v) DEF_GETTER_BY_REF(Duration, utils::Duration, duration_v) #undef DEF_GETTER_BY_REF Value::Value(const Value &other) : type_(other.type_) { switch (other.type_) { case Type::Null: return; case Type::Bool: this->bool_v = other.bool_v; return; case Type::Int: this->int_v = other.int_v; return; case Type::Double: this->double_v = other.double_v; return; case Type::String: new (&string_v) std::string(other.string_v); return; case Type::List: new (&list_v) std::vector(other.list_v); return; case Type::Map: new (&map_v) std::map(other.map_v); return; case Type::Vertex: new (&vertex_v) Vertex(other.vertex_v); return; case Type::Edge: new (&edge_v) Edge(other.edge_v); return; case Type::UnboundedEdge: new (&unbounded_edge_v) UnboundedEdge(other.unbounded_edge_v); return; case Type::Path: new (&path_v) Path(other.path_v); return; case Type::Date: new (&date_v) utils::Date(other.date_v); return; case Type::LocalTime: new (&local_time_v) utils::LocalTime(other.local_time_v); return; case Type::LocalDateTime: new (&local_date_time_v) utils::LocalDateTime(other.local_date_time_v); return; case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); return; } } Value &Value::operator=(const Value &other) { if (this != &other) { this->~Value(); // set the type of this type_ = other.type_; switch (other.type_) { case Type::Null: return *this; case Type::Bool: this->bool_v = other.bool_v; return *this; case Type::Int: this->int_v = other.int_v; return *this; case Type::Double: this->double_v = other.double_v; return *this; case Type::String: new (&string_v) std::string(other.string_v); return *this; case Type::List: new (&list_v) std::vector(other.list_v); return *this; case Type::Map: new (&map_v) std::map(other.map_v); return *this; case Type::Vertex: new (&vertex_v) Vertex(other.vertex_v); return *this; case Type::Edge: new (&edge_v) Edge(other.edge_v); return *this; case Type::UnboundedEdge: new (&unbounded_edge_v) UnboundedEdge(other.unbounded_edge_v); return *this; case Type::Path: new (&path_v) Path(other.path_v); return *this; case Type::Date: new (&date_v) utils::Date(other.date_v); return *this; case Type::LocalTime: new (&local_time_v) utils::LocalTime(other.local_time_v); return *this; case Type::LocalDateTime: new (&local_date_time_v) utils::LocalDateTime(other.local_date_time_v); return *this; case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); return *this; } } return *this; } Value::Value(Value &&other) noexcept : type_(other.type_) { switch (other.type_) { case Type::Null: break; case Type::Bool: this->bool_v = other.bool_v; break; case Type::Int: this->int_v = other.int_v; break; case Type::Double: this->double_v = other.double_v; break; case Type::String: new (&string_v) std::string(std::move(other.string_v)); break; case Type::List: new (&list_v) std::vector(std::move(other.list_v)); break; case Type::Map: new (&map_v) std::map(std::move(other.map_v)); break; case Type::Vertex: new (&vertex_v) Vertex(std::move(other.vertex_v)); break; case Type::Edge: new (&edge_v) Edge(std::move(other.edge_v)); break; case Type::UnboundedEdge: new (&unbounded_edge_v) UnboundedEdge(std::move(other.unbounded_edge_v)); break; case Type::Path: new (&path_v) Path(std::move(other.path_v)); break; case Type::Date: new (&date_v) utils::Date(other.date_v); break; case Type::LocalTime: new (&local_time_v) utils::LocalTime(other.local_time_v); break; case Type::LocalDateTime: new (&local_date_time_v) utils::LocalDateTime(other.local_date_time_v); break; case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); break; } // reset the type of other other.~Value(); other.type_ = Type::Null; } Value &Value::operator=(Value &&other) noexcept { if (this != &other) { this->~Value(); // set the type of this type_ = other.type_; switch (other.type_) { case Type::Null: break; case Type::Bool: this->bool_v = other.bool_v; break; case Type::Int: this->int_v = other.int_v; break; case Type::Double: this->double_v = other.double_v; break; case Type::String: new (&string_v) std::string(std::move(other.string_v)); break; case Type::List: new (&list_v) std::vector(std::move(other.list_v)); break; case Type::Map: new (&map_v) std::map(std::move(other.map_v)); break; case Type::Vertex: new (&vertex_v) Vertex(std::move(other.vertex_v)); break; case Type::Edge: new (&edge_v) Edge(std::move(other.edge_v)); break; case Type::UnboundedEdge: new (&unbounded_edge_v) UnboundedEdge(std::move(other.unbounded_edge_v)); break; case Type::Path: new (&path_v) Path(std::move(other.path_v)); break; case Type::Date: new (&date_v) utils::Date(other.date_v); break; case Type::LocalTime: new (&local_time_v) utils::LocalTime(other.local_time_v); break; case Type::LocalDateTime: new (&local_date_time_v) utils::LocalDateTime(other.local_date_time_v); break; case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); break; } // reset the type of other other.~Value(); other.type_ = Type::Null; } return *this; } Value::~Value() { switch (type_) { // destructor for primitive types does nothing case Type::Null: case Type::Bool: case Type::Int: case Type::Double: return; // we need to call destructors for non primitive types since we used // placement new case Type::String: // Clang fails to compile ~std::string. It seems it is a bug in some // versions of clang. using namespace std statement solves the issue. using namespace std; string_v.~string(); return; case Type::List: using namespace std; list_v.~vector(); return; case Type::Map: using namespace std; map_v.~map(); return; case Type::Vertex: vertex_v.~Vertex(); return; case Type::Edge: edge_v.~Edge(); return; case Type::UnboundedEdge: unbounded_edge_v.~UnboundedEdge(); return; case Type::Path: path_v.~Path(); return; case Type::Date: date_v.~Date(); return; case Type::LocalTime: local_time_v.~LocalTime(); return; case Type::LocalDateTime: local_date_time_v.~LocalDateTime(); return; case Type::Duration: duration_v.~Duration(); return; } } std::ostream &operator<<(std::ostream &os, const Vertex &vertex) { os << "("; if (vertex.labels.size() > 0) { os << ":"; } utils::PrintIterable(os, vertex.labels, ":", [&](auto &stream, auto label) { stream << label; }); if (vertex.labels.size() > 0 && vertex.properties.size() > 0) { os << " "; } if (vertex.properties.size() > 0) { os << "{"; utils::PrintIterable(os, vertex.properties, ", ", [&](auto &stream, const auto &pair) { stream << pair.first << ": " << pair.second; }); os << "}"; } return os << ")"; } std::ostream &operator<<(std::ostream &os, const Edge &edge) { os << "[" << edge.type; if (edge.properties.size() > 0) { os << " {"; utils::PrintIterable(os, edge.properties, ", ", [&](auto &stream, const auto &pair) { stream << pair.first << ": " << pair.second; }); os << "}"; } return os << "]"; } std::ostream &operator<<(std::ostream &os, const UnboundedEdge &edge) { os << "[" << edge.type; if (edge.properties.size() > 0) { os << " {"; utils::PrintIterable(os, edge.properties, ", ", [&](auto &stream, const auto &pair) { stream << pair.first << ": " << pair.second; }); os << "}"; } return os << "]"; } std::ostream &operator<<(std::ostream &os, const Path &path) { os << path.vertices[0]; for (auto it = path.indices.begin(); it != path.indices.end();) { auto edge_ind = *it++; auto vertex_ind = *it++; bool arrow_to_right = true; if (edge_ind < 0) { arrow_to_right = false; edge_ind = -edge_ind; } if (!arrow_to_right) os << "<"; os << "-" << path.edges[edge_ind - 1] << "-"; if (arrow_to_right) os << ">"; os << path.vertices[vertex_ind]; } return os; } std::ostream &operator<<(std::ostream &os, const Value &value) { switch (value.type_) { case Value::Type::Null: return os << "Null"; case Value::Type::Bool: return os << (value.ValueBool() ? "true" : "false"); case Value::Type::Int: return os << value.ValueInt(); case Value::Type::Double: return os << value.ValueDouble(); case Value::Type::String: return os << utils::Escape(value.ValueString()); case Value::Type::List: os << "["; utils::PrintIterable(os, value.ValueList()); return os << "]"; case Value::Type::Map: os << "{"; utils::PrintIterable(os, value.ValueMap(), ", ", [](auto &stream, const auto &pair) { stream << pair.first << ": " << pair.second; }); return os << "}"; case Value::Type::Vertex: return os << value.ValueVertex(); case Value::Type::Edge: return os << value.ValueEdge(); case Value::Type::UnboundedEdge: return os << value.ValueUnboundedEdge(); case Value::Type::Path: return os << value.ValuePath(); case Value::Type::Date: return os << value.ValueDate(); case Value::Type::LocalTime: return os << value.ValueLocalTime(); case Value::Type::LocalDateTime: return os << value.ValueLocalDateTime(); case Value::Type::Duration: return os << value.ValueDuration(); } } std::ostream &operator<<(std::ostream &os, const Value::Type type) { switch (type) { case Value::Type::Null: return os << "null"; case Value::Type::Bool: return os << "bool"; case Value::Type::Int: return os << "int"; case Value::Type::Double: return os << "double"; case Value::Type::String: return os << "string"; case Value::Type::List: return os << "list"; case Value::Type::Map: return os << "map"; case Value::Type::Vertex: return os << "vertex"; case Value::Type::Edge: return os << "edge"; case Value::Type::UnboundedEdge: return os << "unbounded_edge"; case Value::Type::Path: return os << "path"; case Value::Type::Date: return os << "date"; case Value::Type::LocalTime: return os << "local_time"; case Value::Type::LocalDateTime: return os << "local_date_time"; case Value::Type::Duration: return os << "duration"; } } } // namespace memgraph::communication::bolt