Merge branch 'master' into fix-valuetype-function-on-all-data-types
This commit is contained in:
commit
a4f2035943
@ -54,6 +54,10 @@ class EdgeAccessor final {
|
|||||||
return impl_.GetProperty(key, view);
|
return impl_.GetProperty(key, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage::Result<uint64_t> GetPropertySize(storage::PropertyId key, storage::View view) const {
|
||||||
|
return impl_.GetPropertySize(key, view);
|
||||||
|
}
|
||||||
|
|
||||||
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
||||||
return impl_.SetProperty(key, value);
|
return impl_.SetProperty(key, value);
|
||||||
}
|
}
|
||||||
@ -129,6 +133,10 @@ class VertexAccessor final {
|
|||||||
return impl_.GetProperty(key, view);
|
return impl_.GetProperty(key, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage::Result<uint64_t> GetPropertySize(storage::PropertyId key, storage::View view) const {
|
||||||
|
return impl_.GetPropertySize(key, view);
|
||||||
|
}
|
||||||
|
|
||||||
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
||||||
return impl_.SetProperty(key, value);
|
return impl_.SetProperty(key, value);
|
||||||
}
|
}
|
||||||
@ -268,6 +276,10 @@ class SubgraphVertexAccessor final {
|
|||||||
return impl_.GetProperty(view, key);
|
return impl_.GetProperty(view, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage::Result<uint64_t> GetPropertySize(storage::PropertyId key, storage::View view) const {
|
||||||
|
return impl_.GetPropertySize(key, view);
|
||||||
|
}
|
||||||
|
|
||||||
storage::Gid Gid() const noexcept { return impl_.Gid(); }
|
storage::Gid Gid() const noexcept { return impl_.Gid(); }
|
||||||
|
|
||||||
storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); }
|
storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); }
|
||||||
@ -529,6 +541,10 @@ class DbAccessor final {
|
|||||||
|
|
||||||
storage::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
|
storage::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
|
||||||
|
|
||||||
|
std::optional<storage::PropertyId> NameToPropertyIfExists(std::string_view name) const {
|
||||||
|
return accessor_->NameToPropertyIfExists(name);
|
||||||
|
}
|
||||||
|
|
||||||
storage::LabelId NameToLabel(const std::string_view name) { return accessor_->NameToLabel(name); }
|
storage::LabelId NameToLabel(const std::string_view name) { return accessor_->NameToLabel(name); }
|
||||||
|
|
||||||
storage::EdgeTypeId NameToEdgeType(const std::string_view name) { return accessor_->NameToEdgeType(name); }
|
storage::EdgeTypeId NameToEdgeType(const std::string_view name) { return accessor_->NameToEdgeType(name); }
|
||||||
|
@ -447,6 +447,29 @@ TypedValue Size(const TypedValue *args, int64_t nargs, const FunctionContext &ct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypedValue PropertySize(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
||||||
|
FType<Or<Null, Vertex, Edge>, Or<String>>("propertySize", args, nargs);
|
||||||
|
|
||||||
|
auto *dba = ctx.db_accessor;
|
||||||
|
|
||||||
|
const auto &property_name = args[1].ValueString();
|
||||||
|
const auto maybe_property_id = dba->NameToPropertyIfExists(property_name);
|
||||||
|
|
||||||
|
if (!maybe_property_id) {
|
||||||
|
return TypedValue(0, ctx.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t property_size = 0;
|
||||||
|
const auto &graph_entity = args[0];
|
||||||
|
if (graph_entity.IsVertex()) {
|
||||||
|
property_size = graph_entity.ValueVertex().GetPropertySize(*maybe_property_id, ctx.view).GetValue();
|
||||||
|
} else if (graph_entity.IsEdge()) {
|
||||||
|
property_size = graph_entity.ValueEdge().GetPropertySize(*maybe_property_id, ctx.view).GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypedValue(static_cast<int64_t>(property_size), ctx.memory);
|
||||||
|
}
|
||||||
|
|
||||||
TypedValue StartNode(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
TypedValue StartNode(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) {
|
||||||
FType<Or<Null, Edge>>("startNode", args, nargs);
|
FType<Or<Null, Edge>>("startNode", args, nargs);
|
||||||
if (args[0].IsNull()) return TypedValue(ctx.memory);
|
if (args[0].IsNull()) return TypedValue(ctx.memory);
|
||||||
@ -1332,6 +1355,7 @@ std::function<TypedValue(const TypedValue *, int64_t, const FunctionContext &ctx
|
|||||||
if (function_name == "PROPERTIES") return Properties;
|
if (function_name == "PROPERTIES") return Properties;
|
||||||
if (function_name == "RANDOMUUID") return RandomUuid;
|
if (function_name == "RANDOMUUID") return RandomUuid;
|
||||||
if (function_name == "SIZE") return Size;
|
if (function_name == "SIZE") return Size;
|
||||||
|
if (function_name == "PROPERTYSIZE") return PropertySize;
|
||||||
if (function_name == "STARTNODE") return StartNode;
|
if (function_name == "STARTNODE") return StartNode;
|
||||||
if (function_name == "TIMESTAMP") return Timestamp;
|
if (function_name == "TIMESTAMP") return Timestamp;
|
||||||
if (function_name == "TOBOOLEAN") return ToBoolean;
|
if (function_name == "TOBOOLEAN") return ToBoolean;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "storage/v2/delta.hpp"
|
#include "storage/v2/delta.hpp"
|
||||||
#include "storage/v2/mvcc.hpp"
|
#include "storage/v2/mvcc.hpp"
|
||||||
|
#include "storage/v2/property_store.hpp"
|
||||||
#include "storage/v2/property_value.hpp"
|
#include "storage/v2/property_value.hpp"
|
||||||
#include "storage/v2/result.hpp"
|
#include "storage/v2/result.hpp"
|
||||||
#include "storage/v2/storage.hpp"
|
#include "storage/v2/storage.hpp"
|
||||||
@ -264,6 +265,27 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view)
|
|||||||
return *std::move(value);
|
return *std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<uint64_t> EdgeAccessor::GetPropertySize(PropertyId property, View view) const {
|
||||||
|
if (!storage_->config_.salient.items.properties_on_edges) return 0;
|
||||||
|
|
||||||
|
auto guard = std::shared_lock{edge_.ptr->lock};
|
||||||
|
Delta *delta = edge_.ptr->delta;
|
||||||
|
if (!delta) {
|
||||||
|
return edge_.ptr->properties.PropertySize(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto property_result = this->GetProperty(property, view);
|
||||||
|
|
||||||
|
if (property_result.HasError()) {
|
||||||
|
return property_result.GetError();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto property_store = storage::PropertyStore();
|
||||||
|
property_store.SetProperty(property, *property_result);
|
||||||
|
|
||||||
|
return property_store.PropertySize(property);
|
||||||
|
};
|
||||||
|
|
||||||
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view) const {
|
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view) const {
|
||||||
if (!storage_->config_.salient.items.properties_on_edges) return std::map<PropertyId, PropertyValue>{};
|
if (!storage_->config_.salient.items.properties_on_edges) return std::map<PropertyId, PropertyValue>{};
|
||||||
bool exists = true;
|
bool exists = true;
|
||||||
|
@ -82,6 +82,9 @@ class EdgeAccessor final {
|
|||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
||||||
|
|
||||||
|
/// Returns the size of the encoded edge property in bytes.
|
||||||
|
Result<uint64_t> GetPropertySize(PropertyId property, View view) const;
|
||||||
|
|
||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
||||||
|
|
||||||
|
@ -83,6 +83,18 @@ class NameIdMapper {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method unlike NameToId does not insert the new property id if not found
|
||||||
|
/// but just returns either std::nullopt or the value of the property id if it
|
||||||
|
/// finds it.
|
||||||
|
virtual std::optional<uint64_t> NameToIdIfExists(const std::string_view name) {
|
||||||
|
auto name_to_id_acc = name_to_id_.access();
|
||||||
|
auto found = name_to_id_acc.find(name);
|
||||||
|
if (found == name_to_id_acc.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return found->id;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Currently this function returns a `const std::string &` instead of a
|
// NOTE: Currently this function returns a `const std::string &` instead of a
|
||||||
// `std::string` to avoid making unnecessary copies of the string.
|
// `std::string` to avoid making unnecessary copies of the string.
|
||||||
// Usually, this wouldn't be correct because the accessor to the
|
// Usually, this wouldn't be correct because the accessor to the
|
||||||
|
@ -93,6 +93,19 @@ enum class Size : uint8_t {
|
|||||||
INT64 = 0x03,
|
INT64 = 0x03,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint64_t SizeToByteSize(Size size) {
|
||||||
|
switch (size) {
|
||||||
|
case Size::INT8:
|
||||||
|
return 1;
|
||||||
|
case Size::INT16:
|
||||||
|
return 2;
|
||||||
|
case Size::INT32:
|
||||||
|
return 4;
|
||||||
|
case Size::INT64:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All of these values must have the lowest 4 bits set to zero because they are
|
// All of these values must have the lowest 4 bits set to zero because they are
|
||||||
// used to store two `Size` values as described in the comment above.
|
// used to store two `Size` values as described in the comment above.
|
||||||
enum class Type : uint8_t {
|
enum class Type : uint8_t {
|
||||||
@ -486,6 +499,27 @@ std::optional<TemporalData> DecodeTemporalData(Reader &reader) {
|
|||||||
return TemporalData{static_cast<TemporalType>(*type_value), *microseconds_value};
|
return TemporalData{static_cast<TemporalType>(*type_value), *microseconds_value};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||||
|
uint64_t temporal_data_size = 0;
|
||||||
|
|
||||||
|
auto metadata = reader.ReadMetadata();
|
||||||
|
if (!metadata || metadata->type != Type::TEMPORAL_DATA) return std::nullopt;
|
||||||
|
|
||||||
|
temporal_data_size += 1;
|
||||||
|
|
||||||
|
auto type_value = reader.ReadUint(metadata->id_size);
|
||||||
|
if (!type_value) return std::nullopt;
|
||||||
|
|
||||||
|
temporal_data_size += SizeToByteSize(metadata->id_size);
|
||||||
|
|
||||||
|
auto microseconds_value = reader.ReadInt(metadata->payload_size);
|
||||||
|
if (!microseconds_value) return std::nullopt;
|
||||||
|
|
||||||
|
temporal_data_size += SizeToByteSize(metadata->payload_size);
|
||||||
|
|
||||||
|
return temporal_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Function used to decode a PropertyValue from a byte stream.
|
// Function used to decode a PropertyValue from a byte stream.
|
||||||
@ -572,6 +606,92 @@ std::optional<TemporalData> DecodeTemporalData(Reader &reader) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool DecodePropertyValueSize(Reader *reader, Type type, Size payload_size, uint64_t &property_size) {
|
||||||
|
switch (type) {
|
||||||
|
case Type::EMPTY: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Type::NONE:
|
||||||
|
case Type::BOOL: {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Type::INT: {
|
||||||
|
reader->ReadInt(payload_size);
|
||||||
|
property_size += SizeToByteSize(payload_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Type::DOUBLE: {
|
||||||
|
reader->ReadDouble(payload_size);
|
||||||
|
property_size += SizeToByteSize(payload_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Type::STRING: {
|
||||||
|
auto size = reader->ReadUint(payload_size);
|
||||||
|
if (!size) return false;
|
||||||
|
property_size += SizeToByteSize(payload_size);
|
||||||
|
|
||||||
|
std::string str_v(*size, '\0');
|
||||||
|
if (!reader->SkipBytes(*size)) return false;
|
||||||
|
property_size += *size;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Type::LIST: {
|
||||||
|
auto size = reader->ReadUint(payload_size);
|
||||||
|
if (!size) return false;
|
||||||
|
|
||||||
|
uint64_t list_property_size = SizeToByteSize(payload_size);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < *size; ++i) {
|
||||||
|
auto metadata = reader->ReadMetadata();
|
||||||
|
if (!metadata) return false;
|
||||||
|
|
||||||
|
list_property_size += 1;
|
||||||
|
if (!DecodePropertyValueSize(reader, metadata->type, metadata->payload_size, list_property_size)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
property_size += list_property_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Type::MAP: {
|
||||||
|
auto size = reader->ReadUint(payload_size);
|
||||||
|
if (!size) return false;
|
||||||
|
|
||||||
|
uint64_t map_property_size = SizeToByteSize(payload_size);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < *size; ++i) {
|
||||||
|
auto metadata = reader->ReadMetadata();
|
||||||
|
if (!metadata) return false;
|
||||||
|
|
||||||
|
map_property_size += 1;
|
||||||
|
|
||||||
|
auto key_size = reader->ReadUint(metadata->id_size);
|
||||||
|
if (!key_size) return false;
|
||||||
|
|
||||||
|
map_property_size += SizeToByteSize(metadata->id_size);
|
||||||
|
|
||||||
|
std::string key(*key_size, '\0');
|
||||||
|
if (!reader->ReadBytes(key.data(), *key_size)) return false;
|
||||||
|
|
||||||
|
map_property_size += *key_size;
|
||||||
|
|
||||||
|
if (!DecodePropertyValueSize(reader, metadata->type, metadata->payload_size, map_property_size)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
property_size += map_property_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::TEMPORAL_DATA: {
|
||||||
|
const auto maybe_temporal_data_size = DecodeTemporalDataSize(*reader);
|
||||||
|
if (!maybe_temporal_data_size) return false;
|
||||||
|
|
||||||
|
property_size += *maybe_temporal_data_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function used to skip a PropertyValue from a byte stream.
|
// Function used to skip a PropertyValue from a byte stream.
|
||||||
//
|
//
|
||||||
// @sa ComparePropertyValue
|
// @sa ComparePropertyValue
|
||||||
@ -788,6 +908,27 @@ enum class ExpectedPropertyStatus {
|
|||||||
: ExpectedPropertyStatus::GREATER;
|
: ExpectedPropertyStatus::GREATER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ExpectedPropertyStatus DecodeExpectedPropertySize(Reader *reader, PropertyId expected_property,
|
||||||
|
uint64_t &size) {
|
||||||
|
auto metadata = reader->ReadMetadata();
|
||||||
|
if (!metadata) return ExpectedPropertyStatus::MISSING_DATA;
|
||||||
|
|
||||||
|
auto property_id = reader->ReadUint(metadata->id_size);
|
||||||
|
if (!property_id) return ExpectedPropertyStatus::MISSING_DATA;
|
||||||
|
|
||||||
|
if (*property_id == expected_property.AsUint()) {
|
||||||
|
// Add one byte for reading metadata + add the number of bytes for the property key
|
||||||
|
size += (1 + SizeToByteSize(metadata->id_size));
|
||||||
|
if (!DecodePropertyValueSize(reader, metadata->type, metadata->payload_size, size))
|
||||||
|
return ExpectedPropertyStatus::MISSING_DATA;
|
||||||
|
return ExpectedPropertyStatus::EQUAL;
|
||||||
|
}
|
||||||
|
// Don't load the value if this isn't the expected property.
|
||||||
|
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return ExpectedPropertyStatus::MISSING_DATA;
|
||||||
|
return (*property_id < expected_property.AsUint()) ? ExpectedPropertyStatus::SMALLER
|
||||||
|
: ExpectedPropertyStatus::GREATER;
|
||||||
|
}
|
||||||
|
|
||||||
// Function used to check a property exists (PropertyId) from a byte stream.
|
// Function used to check a property exists (PropertyId) from a byte stream.
|
||||||
// It will skip the encoded PropertyValue.
|
// It will skip the encoded PropertyValue.
|
||||||
//
|
//
|
||||||
@ -875,6 +1016,13 @@ enum class ExpectedPropertyStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ExpectedPropertyStatus FindSpecificPropertySize(Reader *reader, PropertyId property, uint64_t &size) {
|
||||||
|
ExpectedPropertyStatus ret = ExpectedPropertyStatus::SMALLER;
|
||||||
|
while ((ret = DecodeExpectedPropertySize(reader, property, size)) == ExpectedPropertyStatus::SMALLER) {
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Function used to find if property is set. It relies on the fact that the properties
|
// Function used to find if property is set. It relies on the fact that the properties
|
||||||
// are sorted (by ID) in the buffer.
|
// are sorted (by ID) in the buffer.
|
||||||
//
|
//
|
||||||
@ -983,6 +1131,31 @@ std::pair<uint64_t, uint8_t *> GetSizeData(const uint8_t *buffer) {
|
|||||||
return {size, data};
|
return {size, data};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BufferInfo {
|
||||||
|
uint64_t size;
|
||||||
|
uint8_t *data{nullptr};
|
||||||
|
bool in_local_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
BufferInfo GetBufferInfo(const uint8_t (&buffer)[N]) {
|
||||||
|
uint64_t size = 0;
|
||||||
|
const uint8_t *data = nullptr;
|
||||||
|
bool in_local_buffer = false;
|
||||||
|
std::tie(size, data) = GetSizeData(buffer);
|
||||||
|
if (size % 8 != 0) {
|
||||||
|
// We are storing the data in the local buffer.
|
||||||
|
size = sizeof(buffer) - 1;
|
||||||
|
data = &buffer[1];
|
||||||
|
in_local_buffer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
||||||
|
auto *non_const_data = const_cast<uint8_t *>(data);
|
||||||
|
|
||||||
|
return {size, non_const_data, in_local_buffer};
|
||||||
|
}
|
||||||
|
|
||||||
void SetSizeData(uint8_t *buffer, uint64_t size, uint8_t *data) {
|
void SetSizeData(uint8_t *buffer, uint64_t size, uint8_t *data) {
|
||||||
memcpy(buffer, &size, sizeof(uint64_t));
|
memcpy(buffer, &size, sizeof(uint64_t));
|
||||||
memcpy(buffer + sizeof(uint64_t), &data, sizeof(uint8_t *));
|
memcpy(buffer + sizeof(uint64_t), &data, sizeof(uint8_t *));
|
||||||
@ -1023,30 +1196,27 @@ PropertyStore::~PropertyStore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PropertyValue PropertyStore::GetProperty(PropertyId property) const {
|
PropertyValue PropertyStore::GetProperty(PropertyId property) const {
|
||||||
uint64_t size;
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
const uint8_t *data;
|
Reader reader(buffer_info.data, buffer_info.size);
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
|
||||||
if (size % 8 != 0) {
|
|
||||||
// We are storing the data in the local buffer.
|
|
||||||
size = sizeof(buffer_) - 1;
|
|
||||||
data = &buffer_[1];
|
|
||||||
}
|
|
||||||
Reader reader(data, size);
|
|
||||||
PropertyValue value;
|
PropertyValue value;
|
||||||
if (FindSpecificProperty(&reader, property, value) != ExpectedPropertyStatus::EQUAL) return {};
|
if (FindSpecificProperty(&reader, property, value) != ExpectedPropertyStatus::EQUAL) return {};
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PropertyStore::HasProperty(PropertyId property) const {
|
uint64_t PropertyStore::PropertySize(PropertyId property) const {
|
||||||
uint64_t size;
|
auto data_size_localbuffer = GetBufferInfo(buffer_);
|
||||||
const uint8_t *data;
|
Reader reader(data_size_localbuffer.data, data_size_localbuffer.size);
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
|
||||||
if (size % 8 != 0) {
|
uint64_t property_size = 0;
|
||||||
// We are storing the data in the local buffer.
|
if (FindSpecificPropertySize(&reader, property, property_size) != ExpectedPropertyStatus::EQUAL) return 0;
|
||||||
size = sizeof(buffer_) - 1;
|
return property_size;
|
||||||
data = &buffer_[1];
|
|
||||||
}
|
}
|
||||||
Reader reader(data, size);
|
|
||||||
|
bool PropertyStore::HasProperty(PropertyId property) const {
|
||||||
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
|
Reader reader(buffer_info.data, buffer_info.size);
|
||||||
|
|
||||||
return ExistsSpecificProperty(&reader, property) == ExpectedPropertyStatus::EQUAL;
|
return ExistsSpecificProperty(&reader, property) == ExpectedPropertyStatus::EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1081,32 +1251,20 @@ std::optional<std::vector<PropertyValue>> PropertyStore::ExtractPropertyValues(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PropertyStore::IsPropertyEqual(PropertyId property, const PropertyValue &value) const {
|
bool PropertyStore::IsPropertyEqual(PropertyId property, const PropertyValue &value) const {
|
||||||
uint64_t size;
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
const uint8_t *data;
|
Reader reader(buffer_info.data, buffer_info.size);
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
|
||||||
if (size % 8 != 0) {
|
|
||||||
// We are storing the data in the local buffer.
|
|
||||||
size = sizeof(buffer_) - 1;
|
|
||||||
data = &buffer_[1];
|
|
||||||
}
|
|
||||||
Reader reader(data, size);
|
|
||||||
auto info = FindSpecificPropertyAndBufferInfo(&reader, property);
|
auto info = FindSpecificPropertyAndBufferInfo(&reader, property);
|
||||||
if (info.property_size == 0) return value.IsNull();
|
if (info.property_size == 0) return value.IsNull();
|
||||||
Reader prop_reader(data + info.property_begin, info.property_size);
|
Reader prop_reader(buffer_info.data + info.property_begin, info.property_size);
|
||||||
if (!CompareExpectedProperty(&prop_reader, property, value)) return false;
|
if (!CompareExpectedProperty(&prop_reader, property, value)) return false;
|
||||||
return prop_reader.GetPosition() == info.property_size;
|
return prop_reader.GetPosition() == info.property_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<PropertyId, PropertyValue> PropertyStore::Properties() const {
|
std::map<PropertyId, PropertyValue> PropertyStore::Properties() const {
|
||||||
uint64_t size;
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
const uint8_t *data;
|
Reader reader(buffer_info.data, buffer_info.size);
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
|
||||||
if (size % 8 != 0) {
|
|
||||||
// We are storing the data in the local buffer.
|
|
||||||
size = sizeof(buffer_) - 1;
|
|
||||||
data = &buffer_[1];
|
|
||||||
}
|
|
||||||
Reader reader(data, size);
|
|
||||||
std::map<PropertyId, PropertyValue> props;
|
std::map<PropertyId, PropertyValue> props;
|
||||||
while (true) {
|
while (true) {
|
||||||
PropertyValue value;
|
PropertyValue value;
|
||||||
@ -1340,33 +1498,20 @@ bool PropertyStore::InitProperties(std::vector<std::pair<storage::PropertyId, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PropertyStore::ClearProperties() {
|
bool PropertyStore::ClearProperties() {
|
||||||
bool in_local_buffer = false;
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
uint64_t size;
|
|
||||||
uint8_t *data;
|
if (!buffer_info.size) return false;
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
if (!buffer_info.in_local_buffer) delete[] buffer_info.data;
|
||||||
if (size % 8 != 0) {
|
|
||||||
// We are storing the data in the local buffer.
|
|
||||||
size = sizeof(buffer_) - 1;
|
|
||||||
data = &buffer_[1];
|
|
||||||
in_local_buffer = true;
|
|
||||||
}
|
|
||||||
if (!size) return false;
|
|
||||||
if (!in_local_buffer) delete[] data;
|
|
||||||
SetSizeData(buffer_, 0, nullptr);
|
SetSizeData(buffer_, 0, nullptr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PropertyStore::StringBuffer() const {
|
std::string PropertyStore::StringBuffer() const {
|
||||||
uint64_t size = 0;
|
BufferInfo buffer_info = GetBufferInfo(buffer_);
|
||||||
const uint8_t *data = nullptr;
|
|
||||||
std::tie(size, data) = GetSizeData(buffer_);
|
std::string arr(buffer_info.size, ' ');
|
||||||
if (size % 8 != 0) { // We are storing the data in the local buffer.
|
for (uint i = 0; i < buffer_info.size; ++i) {
|
||||||
size = sizeof(buffer_) - 1;
|
arr[i] = static_cast<char>(buffer_info.data[i]);
|
||||||
data = &buffer_[1];
|
|
||||||
}
|
|
||||||
std::string arr(size, ' ');
|
|
||||||
for (uint i = 0; i < size; ++i) {
|
|
||||||
arr[i] = static_cast<char>(data[i]);
|
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,11 @@ class PropertyStore {
|
|||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
PropertyValue GetProperty(PropertyId property) const;
|
PropertyValue GetProperty(PropertyId property) const;
|
||||||
|
|
||||||
|
/// Returns the size of the encoded property in bytes.
|
||||||
|
/// Returns 0 if the property does not exist.
|
||||||
|
/// The time complexity of this function is O(n).
|
||||||
|
uint64_t PropertySize(PropertyId property) const;
|
||||||
|
|
||||||
/// Checks whether the property `property` exists in the store. The time
|
/// Checks whether the property `property` exists in the store. The time
|
||||||
/// complexity of this function is O(n).
|
/// complexity of this function is O(n).
|
||||||
bool HasProperty(PropertyId property) const;
|
bool HasProperty(PropertyId property) const;
|
||||||
|
@ -250,6 +250,10 @@ class Storage {
|
|||||||
|
|
||||||
PropertyId NameToProperty(std::string_view name) { return storage_->NameToProperty(name); }
|
PropertyId NameToProperty(std::string_view name) { return storage_->NameToProperty(name); }
|
||||||
|
|
||||||
|
std::optional<PropertyId> NameToPropertyIfExists(std::string_view name) const {
|
||||||
|
return storage_->NameToPropertyIfExists(name);
|
||||||
|
}
|
||||||
|
|
||||||
EdgeTypeId NameToEdgeType(std::string_view name) { return storage_->NameToEdgeType(name); }
|
EdgeTypeId NameToEdgeType(std::string_view name) { return storage_->NameToEdgeType(name); }
|
||||||
|
|
||||||
StorageMode GetCreationStorageMode() const noexcept;
|
StorageMode GetCreationStorageMode() const noexcept;
|
||||||
@ -318,6 +322,14 @@ class Storage {
|
|||||||
return PropertyId::FromUint(name_id_mapper_->NameToId(name));
|
return PropertyId::FromUint(name_id_mapper_->NameToId(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<PropertyId> NameToPropertyIfExists(std::string_view name) const {
|
||||||
|
const auto id = name_id_mapper_->NameToIdIfExists(name);
|
||||||
|
if (!id) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return PropertyId::FromUint(*id);
|
||||||
|
}
|
||||||
|
|
||||||
EdgeTypeId NameToEdgeType(const std::string_view name) const {
|
EdgeTypeId NameToEdgeType(const std::string_view name) const {
|
||||||
return EdgeTypeId::FromUint(name_id_mapper_->NameToId(name));
|
return EdgeTypeId::FromUint(name_id_mapper_->NameToId(name));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -438,6 +438,26 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
|
|||||||
return std::move(value);
|
return std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<uint64_t> VertexAccessor::GetPropertySize(PropertyId property, View view) const {
|
||||||
|
{
|
||||||
|
auto guard = std::shared_lock{vertex_->lock};
|
||||||
|
Delta *delta = vertex_->delta;
|
||||||
|
if (!delta) {
|
||||||
|
return vertex_->properties.PropertySize(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto property_result = this->GetProperty(property, view);
|
||||||
|
if (property_result.HasError()) {
|
||||||
|
return property_result.GetError();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto property_store = storage::PropertyStore();
|
||||||
|
property_store.SetProperty(property, *property_result);
|
||||||
|
|
||||||
|
return property_store.PropertySize(property);
|
||||||
|
};
|
||||||
|
|
||||||
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view) const {
|
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view) const {
|
||||||
bool exists = true;
|
bool exists = true;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -80,6 +80,9 @@ class VertexAccessor final {
|
|||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
Result<PropertyValue> GetProperty(PropertyId property, View view) const;
|
||||||
|
|
||||||
|
/// Returns the size of the encoded vertex property in bytes.
|
||||||
|
Result<uint64_t> GetPropertySize(PropertyId property, View view) const;
|
||||||
|
|
||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ add_subdirectory(queries)
|
|||||||
add_subdirectory(query_modules_storage_modes)
|
add_subdirectory(query_modules_storage_modes)
|
||||||
add_subdirectory(garbage_collection)
|
add_subdirectory(garbage_collection)
|
||||||
add_subdirectory(query_planning)
|
add_subdirectory(query_planning)
|
||||||
|
add_subdirectory(awesome_functions)
|
||||||
|
|
||||||
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||||
add_subdirectory(high_availability_experimental)
|
add_subdirectory(high_availability_experimental)
|
||||||
|
6
tests/e2e/awesome_functions/CMakeLists.txt
Normal file
6
tests/e2e/awesome_functions/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function(copy_awesome_functions_e2e_python_files FILE_NAME)
|
||||||
|
copy_e2e_python_files(awesome_functions ${FILE_NAME})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
copy_awesome_functions_e2e_python_files(common.py)
|
||||||
|
copy_awesome_functions_e2e_python_files(awesome_functions.py)
|
269
tests/e2e/awesome_functions/awesome_functions.py
Normal file
269
tests/e2e/awesome_functions/awesome_functions.py
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
# Copyright 2023 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.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from common import get_bytes, memgraph
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_null_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.null_prop = null;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
null_bytes = get_bytes(memgraph, "null_prop")
|
||||||
|
|
||||||
|
# No property stored, no bytes allocated
|
||||||
|
assert null_bytes == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_bool_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.bool_prop = True;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
bool_bytes = get_bytes(memgraph, "bool_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id, but value is encoded in the metadata
|
||||||
|
assert bool_bytes == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_one_byte_int_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.S_int_prop = 4;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
s_int_bytes = get_bytes(memgraph, "S_int_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id + payload size 1 byte to store the int
|
||||||
|
assert s_int_bytes == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_two_byte_int_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.M_int_prop = 500;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
m_int_bytes = get_bytes(memgraph, "M_int_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id + payload size 2 bytes to store the int
|
||||||
|
assert m_int_bytes == 4
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_four_byte_int_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.L_int_prop = 1000000000;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
l_int_bytes = get_bytes(memgraph, "L_int_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id + payload size 4 bytes to store the int
|
||||||
|
assert l_int_bytes == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_eight_byte_int_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.XL_int_prop = 1000000000000;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
xl_int_bytes = get_bytes(memgraph, "XL_int_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id + payload size 8 bytes to store the int
|
||||||
|
assert xl_int_bytes == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_float_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.float_prop = 4.0;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
float_bytes = get_bytes(memgraph, "float_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata, 1 byte prop id + payload size 8 bytes to store the float
|
||||||
|
assert float_bytes == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_string_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.str_prop = 'str_value';
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
str_bytes = get_bytes(memgraph, "str_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata
|
||||||
|
# 1 byte prop id
|
||||||
|
# - the payload size contains the amount of bytes stored for the size in the next sequence
|
||||||
|
# X bytes for the length of the string (1, 2, 4 or 8 bytes) -> "str_value" has 1 byte for the length of 9
|
||||||
|
# Y bytes for the string content -> 9 bytes for "str_value"
|
||||||
|
assert str_bytes == 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_list_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.list_prop = [1, 2, 3];
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
list_bytes = get_bytes(memgraph, "list_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata
|
||||||
|
# 1 byte prop id
|
||||||
|
# - the payload size contains the amount of bytes stored for the size of the list
|
||||||
|
# X bytes for the size of the list (1, 2, 4 or 8 bytes)
|
||||||
|
# for each list element:
|
||||||
|
# - 1 byte for the metadata
|
||||||
|
# - the amount of bytes for the payload of the type (a small int is 1 additional byte)
|
||||||
|
# in this case 1 + 1 + 3 * (1 + 1)
|
||||||
|
assert list_bytes == 9
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_map_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.map_prop = {key1: 'value', key2: 4};
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
map_bytes = get_bytes(memgraph, "map_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata
|
||||||
|
# 1 byte prop id
|
||||||
|
# - the payload size contains the amount of bytes stored for the size of the map
|
||||||
|
# X bytes for the size of the map (1, 2, 4 or 8 bytes - in this case 1)
|
||||||
|
# for every map element:
|
||||||
|
# - 1 byte for metadata
|
||||||
|
# - 1, 2, 4 or 8 bytes for the key length (read from the metadata payload) -> this case 1
|
||||||
|
# - Y bytes for the key content -> this case 4
|
||||||
|
# - Z amount of bytes for the type
|
||||||
|
# - for 'value' -> 1 byte for size and 5 for length
|
||||||
|
# - for 4 -> 1 byte for content read from payload
|
||||||
|
# total: 1 + 1 + (1 + 1 + 4 + (1 + 5)) + (1 + 1 + 4 + (1))
|
||||||
|
assert map_bytes == 22
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_date_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.date_prop = date('2023-01-01');
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
date_bytes = get_bytes(memgraph, "date_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata (to see that it's temporal data)
|
||||||
|
# 1 byte prop id
|
||||||
|
# 1 byte metadata
|
||||||
|
# - type is again the same
|
||||||
|
# - id field contains the length of the specific temporal type (1, 2, 4 or 8 bytes) -> probably always 1
|
||||||
|
# - payload field contains the length of the microseconds (1, 2, 4, or 8 bytes) -> probably always 8
|
||||||
|
assert date_bytes == 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_local_time_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.localtime_prop = localtime('23:00:00');
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
local_time_bytes = get_bytes(memgraph, "localtime_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata (to see that it's temporal data)
|
||||||
|
# 1 byte prop id
|
||||||
|
# 1 byte metadata
|
||||||
|
# - type is again the same
|
||||||
|
# - id field contains the length of the specific temporal type (1, 2, 4 or 8 bytes) -> probably always 1
|
||||||
|
# - payload field contains the length of the microseconds (1, 2, 4, or 8 bytes) -> probably always 8
|
||||||
|
assert local_time_bytes == 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_local_date_time_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.localdatetime_prop = localdatetime('2022-01-01T23:00:00');
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
local_date_time_bytes = get_bytes(memgraph, "localdatetime_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata (to see that it's temporal data)
|
||||||
|
# 1 byte prop id
|
||||||
|
# 1 byte metadata
|
||||||
|
# - type is again the same
|
||||||
|
# - id field contains the length of the specific temporal type (1, 2, 4 or 8 bytes) -> probably always 1
|
||||||
|
# - payload field contains the length of the microseconds (1, 2, 4, or 8 bytes) -> probably always 8
|
||||||
|
assert local_date_time_bytes == 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_duration_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node)
|
||||||
|
SET n.duration_prop = duration('P5DT2M2.33S');
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
duration_bytes = get_bytes(memgraph, "duration_prop")
|
||||||
|
|
||||||
|
# 1 byte metadata (to see that it's temporal data)
|
||||||
|
# 1 byte prop id
|
||||||
|
# 1 byte metadata
|
||||||
|
# - type is again the same
|
||||||
|
# - id field contains the length of the specific temporal type (1, 2, 4 or 8 bytes) -> probably always 1
|
||||||
|
# - payload field contains the length of the microseconds (1, 2, 4, or 8 bytes) -> probably always 8
|
||||||
|
assert duration_bytes == 12
|
||||||
|
|
||||||
|
|
||||||
|
def test_property_size_on_nonexistent_prop(memgraph):
|
||||||
|
memgraph.execute(
|
||||||
|
"""
|
||||||
|
CREATE (n:Node);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
nonexistent_bytes = get_bytes(memgraph, "nonexistent_prop")
|
||||||
|
|
||||||
|
assert nonexistent_bytes == 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
29
tests/e2e/awesome_functions/common.py
Normal file
29
tests/e2e/awesome_functions/common.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright 2023 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.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from gqlalchemy import Memgraph
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def memgraph(**kwargs) -> Memgraph:
|
||||||
|
memgraph = Memgraph()
|
||||||
|
|
||||||
|
yield memgraph
|
||||||
|
|
||||||
|
memgraph.drop_indexes()
|
||||||
|
memgraph.ensure_constraints([])
|
||||||
|
memgraph.drop_database()
|
||||||
|
|
||||||
|
|
||||||
|
def get_bytes(memgraph, prop_name):
|
||||||
|
res = list(memgraph.execute_and_fetch(f"MATCH (n) RETURN propertySize(n, '{prop_name}') AS size"))
|
||||||
|
return res[0]["size"]
|
14
tests/e2e/awesome_functions/workloads.yaml
Normal file
14
tests/e2e/awesome_functions/workloads.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
awesome_functions_cluster: &awesome_functions_cluster
|
||||||
|
cluster:
|
||||||
|
main:
|
||||||
|
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||||
|
log_file: "awesome_functions.log"
|
||||||
|
setup_queries: []
|
||||||
|
validation_queries: []
|
||||||
|
|
||||||
|
|
||||||
|
workloads:
|
||||||
|
- name: "Awesome Functions"
|
||||||
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
args: ["awesome_functions/awesome_functions.py"]
|
||||||
|
<<: *awesome_functions_cluster
|
Loading…
Reference in New Issue
Block a user