Compare commits
8 Commits
forbid-mer
...
T580-FL-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11edd65757 | ||
|
|
8c58eddd54 | ||
|
|
13c33149e5 | ||
|
|
af519d517a | ||
|
|
49eb6390ff | ||
|
|
b9ae685c48 | ||
|
|
41622cb765 | ||
|
|
a34a2f4140 |
@@ -52,7 +52,7 @@ target_link_libraries(memgraph ${mg_single_node_v2_libs})
|
||||
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
|
||||
# should be dynamically exported, so that `dlopen` can correctly link the
|
||||
# symbols in custom procedure module libraries.
|
||||
target_link_libraries(memgraph "-Wl,--dynamic-list=${CMAKE_SOURCE_DIR}/include/mg_procedure.syms")
|
||||
target_link_libraries(memgraph "-Wl,--dynamic-list=${CMAKE_SOURCE_DIR}/include/mg_procedure.syms" "-lprofiler")
|
||||
set_target_properties(memgraph PROPERTIES
|
||||
|
||||
# Set the executable output name to include version information.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -125,6 +125,11 @@ class VertexAccessor final {
|
||||
return impl_.SetProperty(key, value);
|
||||
}
|
||||
|
||||
storage::Result<std::vector<storage::PropertyValue>> SetProperties(
|
||||
std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties) {
|
||||
return impl_.SetProperties(properties);
|
||||
}
|
||||
|
||||
storage::Result<storage::PropertyValue> RemoveProperty(storage::PropertyId key) {
|
||||
return SetProperty(key, storage::PropertyValue());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -31,6 +31,78 @@
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
static std::chrono::duration<double> total2;
|
||||
static std::chrono::duration<double> new_print2;
|
||||
|
||||
class ReferenceExpressionEvaluator : public ExpressionVisitor<TypedValue *> {
|
||||
public:
|
||||
ReferenceExpressionEvaluator(Frame *frame, const SymbolTable *symbol_table, const EvaluationContext *ctx,
|
||||
DbAccessor *dba, storage::View view)
|
||||
: frame_(frame), symbol_table_(symbol_table), ctx_(ctx), dba_(dba), view_(view) {}
|
||||
|
||||
using ExpressionVisitor<TypedValue *>::Visit;
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const { return ctx_->memory; }
|
||||
|
||||
#define UNSUCCESFUL_VISIT(expr_name) \
|
||||
TypedValue *Visit(expr_name &expr) override { return nullptr; }
|
||||
|
||||
TypedValue *Visit(Identifier &ident) override { return &frame_->at(symbol_table_->at(ident)); }
|
||||
|
||||
UNSUCCESFUL_VISIT(NamedExpression);
|
||||
UNSUCCESFUL_VISIT(OrOperator);
|
||||
UNSUCCESFUL_VISIT(XorOperator);
|
||||
UNSUCCESFUL_VISIT(AdditionOperator);
|
||||
UNSUCCESFUL_VISIT(SubtractionOperator);
|
||||
UNSUCCESFUL_VISIT(MultiplicationOperator);
|
||||
UNSUCCESFUL_VISIT(DivisionOperator);
|
||||
UNSUCCESFUL_VISIT(ModOperator);
|
||||
UNSUCCESFUL_VISIT(NotEqualOperator);
|
||||
UNSUCCESFUL_VISIT(EqualOperator);
|
||||
UNSUCCESFUL_VISIT(LessOperator);
|
||||
UNSUCCESFUL_VISIT(GreaterOperator);
|
||||
UNSUCCESFUL_VISIT(LessEqualOperator);
|
||||
UNSUCCESFUL_VISIT(GreaterEqualOperator);
|
||||
|
||||
UNSUCCESFUL_VISIT(NotOperator);
|
||||
UNSUCCESFUL_VISIT(UnaryPlusOperator);
|
||||
UNSUCCESFUL_VISIT(UnaryMinusOperator);
|
||||
|
||||
UNSUCCESFUL_VISIT(AndOperator);
|
||||
UNSUCCESFUL_VISIT(IfOperator);
|
||||
UNSUCCESFUL_VISIT(InListOperator);
|
||||
|
||||
UNSUCCESFUL_VISIT(SubscriptOperator);
|
||||
|
||||
UNSUCCESFUL_VISIT(ListSlicingOperator);
|
||||
UNSUCCESFUL_VISIT(IsNullOperator);
|
||||
UNSUCCESFUL_VISIT(PropertyLookup);
|
||||
UNSUCCESFUL_VISIT(LabelsTest);
|
||||
|
||||
UNSUCCESFUL_VISIT(PrimitiveLiteral);
|
||||
UNSUCCESFUL_VISIT(ListLiteral);
|
||||
UNSUCCESFUL_VISIT(MapLiteral);
|
||||
UNSUCCESFUL_VISIT(Aggregation);
|
||||
UNSUCCESFUL_VISIT(Coalesce);
|
||||
UNSUCCESFUL_VISIT(Function);
|
||||
UNSUCCESFUL_VISIT(Reduce);
|
||||
UNSUCCESFUL_VISIT(Extract);
|
||||
UNSUCCESFUL_VISIT(All);
|
||||
UNSUCCESFUL_VISIT(Single);
|
||||
UNSUCCESFUL_VISIT(Any);
|
||||
UNSUCCESFUL_VISIT(None);
|
||||
UNSUCCESFUL_VISIT(ParameterLookup);
|
||||
UNSUCCESFUL_VISIT(RegexMatch);
|
||||
|
||||
private:
|
||||
Frame *frame_;
|
||||
const SymbolTable *symbol_table_;
|
||||
const EvaluationContext *ctx_;
|
||||
DbAccessor *dba_;
|
||||
// which switching approach should be used when evaluating
|
||||
storage::View view_;
|
||||
};
|
||||
|
||||
class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
public:
|
||||
ExpressionEvaluator(Frame *frame, const SymbolTable &symbol_table, const EvaluationContext &ctx, DbAccessor *dba,
|
||||
@@ -159,49 +231,51 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
TypedValue Visit(SubscriptOperator &list_indexing) override {
|
||||
auto lhs = list_indexing.expression1_->Accept(*this);
|
||||
ReferenceExpressionEvaluator referenceExpressionEvaluator(frame_, symbol_table_, ctx_, dba_, view_);
|
||||
|
||||
auto *lhs = list_indexing.expression1_->Accept(referenceExpressionEvaluator);
|
||||
auto index = list_indexing.expression2_->Accept(*this);
|
||||
if (!lhs.IsList() && !lhs.IsMap() && !lhs.IsVertex() && !lhs.IsEdge() && !lhs.IsNull())
|
||||
if (!lhs->IsList() && !lhs->IsMap() && !lhs->IsVertex() && !lhs->IsEdge() && !lhs->IsNull())
|
||||
throw QueryRuntimeException(
|
||||
"Expected a list, a map, a node or an edge to index with '[]', got "
|
||||
"{}.",
|
||||
lhs.type());
|
||||
if (lhs.IsNull() || index.IsNull()) return TypedValue(ctx_->memory);
|
||||
if (lhs.IsList()) {
|
||||
lhs->type());
|
||||
if (lhs->IsNull() || index.IsNull()) return TypedValue(ctx_->memory);
|
||||
if (lhs->IsList()) {
|
||||
if (!index.IsInt()) throw QueryRuntimeException("Expected an integer as a list index, got {}.", index.type());
|
||||
auto index_int = index.ValueInt();
|
||||
// NOTE: Take non-const reference to list, so that we can move out the
|
||||
// indexed element as the result.
|
||||
auto &list = lhs.ValueList();
|
||||
const auto &list = lhs->ValueList();
|
||||
if (index_int < 0) {
|
||||
index_int += static_cast<int64_t>(list.size());
|
||||
}
|
||||
if (index_int >= static_cast<int64_t>(list.size()) || index_int < 0) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(list[index_int]);
|
||||
return TypedValue(list[index_int]);
|
||||
}
|
||||
|
||||
if (lhs.IsMap()) {
|
||||
if (lhs->IsMap()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a map index, got {}.", index.type());
|
||||
// NOTE: Take non-const reference to map, so that we can move out the
|
||||
// looked-up element as the result.
|
||||
auto &map = lhs.ValueMap();
|
||||
const auto &map = lhs->ValueMap();
|
||||
auto found = map.find(index.ValueString());
|
||||
if (found == map.end()) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(found->second);
|
||||
return TypedValue(found->second);
|
||||
}
|
||||
|
||||
if (lhs.IsVertex()) {
|
||||
if (lhs->IsVertex()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a property name, got {}.", index.type());
|
||||
return TypedValue(GetProperty(lhs.ValueVertex(), index.ValueString()), ctx_->memory);
|
||||
return TypedValue(GetProperty(lhs->ValueVertex(), index.ValueString()), ctx_->memory);
|
||||
}
|
||||
|
||||
if (lhs.IsEdge()) {
|
||||
if (lhs->IsEdge()) {
|
||||
if (!index.IsString()) throw QueryRuntimeException("Expected a string as a property name, got {}.", index.type());
|
||||
return TypedValue(GetProperty(lhs.ValueEdge(), index.ValueString()), ctx_->memory);
|
||||
return TypedValue(GetProperty(lhs->ValueEdge(), index.ValueString()), ctx_->memory);
|
||||
}
|
||||
|
||||
// lhs is Null
|
||||
@@ -258,7 +332,10 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
TypedValue Visit(PropertyLookup &property_lookup) override {
|
||||
auto expression_result = property_lookup.expression_->Accept(*this);
|
||||
ReferenceExpressionEvaluator referenceExpressionEvaluator(frame_, symbol_table_, ctx_, dba_, view_);
|
||||
|
||||
auto *expression_result = property_lookup.expression_->Accept(referenceExpressionEvaluator);
|
||||
// auto expression_result = property_lookup.expression_->Accept(*this);
|
||||
auto maybe_date = [this](const auto &date, const auto &prop_name) -> std::optional<TypedValue> {
|
||||
if (prop_name == "year") {
|
||||
return TypedValue(date.year, ctx_->memory);
|
||||
@@ -332,42 +409,42 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
switch (expression_result.type()) {
|
||||
switch (expression_result->type()) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue(ctx_->memory);
|
||||
case TypedValue::Type::Vertex:
|
||||
return TypedValue(GetProperty(expression_result.ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
return TypedValue(GetProperty(expression_result->ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Edge:
|
||||
return TypedValue(GetProperty(expression_result.ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
return TypedValue(GetProperty(expression_result->ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Map: {
|
||||
// NOTE: Take non-const reference to map, so that we can move out the
|
||||
// looked-up element as the result.
|
||||
auto &map = expression_result.ValueMap();
|
||||
auto &map = expression_result->ValueMap();
|
||||
auto found = map.find(property_lookup.property_.name.c_str());
|
||||
if (found == map.end()) return TypedValue(ctx_->memory);
|
||||
// NOTE: Explicit move is needed, so that we return the move constructed
|
||||
// value and preserve the correct MemoryResource.
|
||||
return std::move(found->second);
|
||||
return TypedValue(found->second, ctx_->memory);
|
||||
}
|
||||
case TypedValue::Type::Duration: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &dur = expression_result.ValueDuration();
|
||||
const auto &dur = expression_result->ValueDuration();
|
||||
if (auto dur_field = maybe_duration(dur, prop_name); dur_field) {
|
||||
return std::move(*dur_field);
|
||||
return TypedValue(*dur_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Duration", prop_name);
|
||||
}
|
||||
case TypedValue::Type::Date: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &date = expression_result.ValueDate();
|
||||
const auto &date = expression_result->ValueDate();
|
||||
if (auto date_field = maybe_date(date, prop_name); date_field) {
|
||||
return std::move(*date_field);
|
||||
return TypedValue(*date_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Date", prop_name);
|
||||
}
|
||||
case TypedValue::Type::LocalTime: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto < = expression_result.ValueLocalTime();
|
||||
const auto < = expression_result->ValueLocalTime();
|
||||
if (auto lt_field = maybe_local_time(lt, prop_name); lt_field) {
|
||||
return std::move(*lt_field);
|
||||
}
|
||||
@@ -375,20 +452,20 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
case TypedValue::Type::LocalDateTime: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &ldt = expression_result.ValueLocalDateTime();
|
||||
const auto &ldt = expression_result->ValueLocalDateTime();
|
||||
if (auto date_field = maybe_date(ldt.date, prop_name); date_field) {
|
||||
return std::move(*date_field);
|
||||
}
|
||||
if (auto lt_field = maybe_local_time(ldt.local_time, prop_name); lt_field) {
|
||||
return std::move(*lt_field);
|
||||
return TypedValue(*lt_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for LocalDateTime", prop_name);
|
||||
}
|
||||
case TypedValue::Type::Graph: {
|
||||
const auto &prop_name = property_lookup.property_.name;
|
||||
const auto &graph = expression_result.ValueGraph();
|
||||
const auto &graph = expression_result->ValueGraph();
|
||||
if (auto graph_field = maybe_graph(graph, prop_name); graph_field) {
|
||||
return std::move(*graph_field);
|
||||
return TypedValue(*graph_field, ctx_->memory);
|
||||
}
|
||||
throw QueryRuntimeException("Invalid property name {} for Graph", prop_name);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -32,7 +32,7 @@ struct Parameters {
|
||||
* @param position Token position in query of value.
|
||||
* @param value
|
||||
*/
|
||||
void Add(int position, const storage::PropertyValue &value) { storage_.emplace_back(position, value); }
|
||||
void Add(int position, const storage::PropertyValue &value) { storage_.emplace(position, value); }
|
||||
|
||||
/**
|
||||
* Returns the value found for the given token position.
|
||||
@@ -41,9 +41,8 @@ struct Parameters {
|
||||
* @return Value for the given token position.
|
||||
*/
|
||||
const storage::PropertyValue &AtTokenPosition(int position) const {
|
||||
auto found = std::find_if(storage_.begin(), storage_.end(), [&](const auto &a) { return a.first == position; });
|
||||
MG_ASSERT(found != storage_.end(), "Token position must be present in container");
|
||||
return found->second;
|
||||
MG_ASSERT(storage_.contains(position), "Token position must be present in container");
|
||||
return storage_.at(position);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,9 +52,9 @@ struct Parameters {
|
||||
* @param position Which stripped param is sought.
|
||||
* @return Token position and value for sought param.
|
||||
*/
|
||||
const std::pair<int, storage::PropertyValue> &At(int position) const {
|
||||
const storage::PropertyValue &At(int position) const {
|
||||
MG_ASSERT(position < static_cast<int>(storage_.size()), "Invalid position");
|
||||
return storage_[position];
|
||||
return storage_.at(position);
|
||||
}
|
||||
|
||||
/** Returns the number of arguments in this container */
|
||||
@@ -65,7 +64,7 @@ struct Parameters {
|
||||
auto end() const { return storage_.end(); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<int, storage::PropertyValue>> storage_;
|
||||
std::unordered_map<int, storage::PropertyValue> storage_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -155,6 +155,9 @@ uint64_t ComputeProfilingKey(const T *obj) {
|
||||
|
||||
#define SCOPED_PROFILE_OP(name) ScopedProfile profile{ComputeProfilingKey(this), name, &context};
|
||||
|
||||
std::chrono::duration<double> total;
|
||||
std::chrono::duration<double> new_print;
|
||||
|
||||
bool Once::OnceCursor::Pull(Frame &, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("Once");
|
||||
|
||||
@@ -208,10 +211,14 @@ VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *fram
|
||||
storage::View::NEW);
|
||||
// TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory
|
||||
// when we update PropertyValue with custom allocator.
|
||||
|
||||
std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> properties;
|
||||
if (const auto *node_info_properties = std::get_if<PropertiesMapList>(&node_info.properties)) {
|
||||
for (const auto &[key, value_expression] : *node_info_properties) {
|
||||
PropsSetChecked(&new_node, key, value_expression->Accept(evaluator));
|
||||
properties.emplace_back(key, value_expression->Accept(evaluator));
|
||||
// PropsSetChecked(&new_node, key, );
|
||||
}
|
||||
new_node.SetProperties(properties);
|
||||
} else {
|
||||
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties));
|
||||
for (const auto &[key, value] : property_map.ValueMap()) {
|
||||
@@ -4579,6 +4586,7 @@ class LoadCsvCursor : public Cursor {
|
||||
// have to read at most cardinality(n) rows (but we can read less and stop
|
||||
// pulling MATCH).
|
||||
if (!input_is_once_ && !input_pulled) return false;
|
||||
|
||||
auto row = reader_->GetNextRow(context.evaluation_context.memory);
|
||||
if (!row) {
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
std::chrono::duration<double> total;
|
||||
std::chrono::duration<double> new_print;
|
||||
|
||||
TypedValue::TypedValue(const storage::PropertyValue &value)
|
||||
// TODO: MemoryResource in storage::PropertyValue
|
||||
: TypedValue(value, utils::NewDeleteResource()) {}
|
||||
@@ -289,8 +292,18 @@ TypedValue::operator storage::PropertyValue() const {
|
||||
return storage::PropertyValue(int_v);
|
||||
case TypedValue::Type::Double:
|
||||
return storage::PropertyValue(double_v);
|
||||
case TypedValue::Type::String:
|
||||
return storage::PropertyValue(std::string(string_v));
|
||||
case TypedValue::Type::String: {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
auto value = storage::PropertyValue(std::string(string_v));
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
std::chrono::duration<double> dif = end - start;
|
||||
total += dif;
|
||||
if (total > new_print) {
|
||||
std::cout << "Time typed value difference = " << total.count() << "[s]" << std::endl;
|
||||
new_print += static_cast<std::chrono::duration<double>>(10.0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
case TypedValue::Type::List:
|
||||
return storage::PropertyValue(std::vector<storage::PropertyValue>(list_v.begin(), list_v.end()));
|
||||
case TypedValue::Type::Map: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -869,6 +869,26 @@ SpecificPropertyAndBufferInfo FindSpecificPropertyAndBufferInfo(Reader *reader,
|
||||
return {property_begin, property_end, property_end - property_begin, all_begin, all_end, all_end - all_begin};
|
||||
}
|
||||
|
||||
// Function used to move to the position where the property should be in the data
|
||||
// buffer.
|
||||
// The `property_size` will be `0`
|
||||
// and `property_begin` will be equal to `property_end`. Positions and size of
|
||||
// all properties is always calculated (even if the specific property isn't
|
||||
// found).
|
||||
//
|
||||
// @sa FindSpecificProperty
|
||||
SpecificPropertyAndBufferInfo SkipReaderAndGetBufferInfo(Reader *reader, uint64_t size) {
|
||||
uint64_t property_begin = reader->GetPosition();
|
||||
uint64_t property_end = reader->GetPosition();
|
||||
uint64_t all_begin = reader->GetPosition();
|
||||
uint64_t all_end = reader->GetPosition();
|
||||
reader->SkipBytes(size);
|
||||
property_begin = reader->GetPosition();
|
||||
property_end = reader->GetPosition();
|
||||
all_end = reader->GetPosition();
|
||||
return {property_begin, property_end, property_end - property_begin, all_begin, all_end, all_end - all_begin};
|
||||
}
|
||||
|
||||
// All data buffers will be allocated to a power of 8 size.
|
||||
uint64_t ToPowerOf8(uint64_t size) {
|
||||
uint64_t mod = size % 8;
|
||||
@@ -971,6 +991,8 @@ PropertyValue PropertyStore::GetProperty(PropertyId property) const {
|
||||
return value;
|
||||
}
|
||||
|
||||
PropertyValue PropertyStore::GetEmptyProperty() const { return PropertyValue(); }
|
||||
|
||||
bool PropertyStore::HasProperty(PropertyId property) const {
|
||||
uint64_t size;
|
||||
const uint8_t *data;
|
||||
@@ -1144,6 +1166,57 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
return !existed;
|
||||
}
|
||||
|
||||
// use this function only when inserting new properties
|
||||
bool PropertyStore::SetProperties(std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties) {
|
||||
uint64_t size;
|
||||
uint8_t *data;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
MG_ASSERT(size == 0, "Invalid database state!");
|
||||
|
||||
uint64_t property_size = 0;
|
||||
{
|
||||
Writer writer;
|
||||
for (const auto &[property, value] : properties) {
|
||||
EncodeProperty(&writer, property, value);
|
||||
property_size = writer.Written();
|
||||
}
|
||||
}
|
||||
|
||||
auto property_size_to_power_of_8 = ToPowerOf8(property_size);
|
||||
if (property_size <= sizeof(buffer_) - 1) {
|
||||
// Use the local buffer.
|
||||
buffer_[0] = kUseLocalBuffer;
|
||||
size = sizeof(buffer_) - 1;
|
||||
data = &buffer_[1];
|
||||
} else {
|
||||
// Allocate a new external buffer.
|
||||
auto alloc_data = new uint8_t[property_size_to_power_of_8];
|
||||
auto alloc_size = property_size_to_power_of_8;
|
||||
|
||||
SetSizeData(buffer_, alloc_size, alloc_data);
|
||||
|
||||
size = alloc_size;
|
||||
data = alloc_data;
|
||||
}
|
||||
|
||||
// Encode the property into the data buffer.
|
||||
Writer writer(data, size);
|
||||
|
||||
for (const auto &[property, value] : properties) {
|
||||
MG_ASSERT(EncodeProperty(&writer, property, value), "Invalid database state!");
|
||||
property_size = writer.Written();
|
||||
}
|
||||
|
||||
auto metadata = writer.WriteMetadata();
|
||||
if (metadata) {
|
||||
// If there is any space left in the buffer we add a tombstone to
|
||||
// indicate that there are no more properties to be decoded.
|
||||
metadata->Set({Type::EMPTY});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyStore::ClearProperties() {
|
||||
bool in_local_buffer = false;
|
||||
uint64_t size;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -42,6 +42,8 @@ class PropertyStore {
|
||||
/// complexity of this function is O(n).
|
||||
bool HasProperty(PropertyId property) const;
|
||||
|
||||
PropertyValue GetEmptyProperty() const;
|
||||
|
||||
/// Checks whether the property `property` is equal to the specified value
|
||||
/// `value`. This function doesn't perform any memory allocations while
|
||||
/// performing the equality check. The time complexity of this function is
|
||||
@@ -59,6 +61,8 @@ class PropertyStore {
|
||||
/// @throw std::bad_alloc
|
||||
bool SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
bool SetProperties(std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties);
|
||||
|
||||
/// Remove all properties and return `true` if any removal took place.
|
||||
/// `false` is returned if there were no properties to remove. The time
|
||||
/// complexity of this function is O(1).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <chrono>
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/indices.hpp"
|
||||
@@ -60,6 +61,9 @@ std::pair<bool, bool> IsVisible(Vertex *vertex, Transaction *transaction, View v
|
||||
} // namespace
|
||||
} // namespace detail
|
||||
|
||||
std::chrono::duration<double> total;
|
||||
std::chrono::duration<double> new_print;
|
||||
|
||||
std::optional<VertexAccessor> VertexAccessor::Create(Vertex *vertex, Transaction *transaction, Indices *indices,
|
||||
Constraints *constraints, Config::Items config, View view) {
|
||||
if (const auto [exists, deleted] = detail::IsVisible(vertex, transaction, view); !exists || deleted) {
|
||||
@@ -208,6 +212,8 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
}
|
||||
|
||||
Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) {
|
||||
// auto start = std::chrono::steady_clock::now();
|
||||
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
@@ -227,9 +233,44 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
|
||||
|
||||
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
|
||||
|
||||
// auto end = std::chrono::steady_clock::now();
|
||||
// std::chrono::duration<double> dif = end - start;
|
||||
// total += dif;
|
||||
|
||||
// if (total > new_print) {
|
||||
// std::cout << "Time difference = " << total.count() << "[s]" << std::endl;
|
||||
// new_print += static_cast<std::chrono::duration<double>>(2.0);
|
||||
// }
|
||||
|
||||
return std::move(current_value);
|
||||
}
|
||||
|
||||
Result<std::vector<storage::PropertyValue>> VertexAccessor::SetProperties(
|
||||
std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties) {
|
||||
// Be careful when calling this function
|
||||
// It will set properties in batch, without checking if property already exists
|
||||
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
std::vector<storage::PropertyValue> new_values;
|
||||
|
||||
vertex_->properties.SetProperties(properties);
|
||||
|
||||
for (const auto &[property, value] : properties) {
|
||||
auto current_value = vertex_->properties.GetEmptyProperty();
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
|
||||
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
|
||||
new_values.emplace_back(current_value);
|
||||
}
|
||||
|
||||
return new_values;
|
||||
}
|
||||
|
||||
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -68,6 +68,9 @@ class VertexAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
Result<std::vector<storage::PropertyValue>> SetProperties(
|
||||
std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties);
|
||||
|
||||
/// Remove all properties and return the values of the removed properties.
|
||||
/// @throw std::bad_alloc
|
||||
Result<std::map<PropertyId, PropertyValue>> ClearProperties();
|
||||
|
||||
Reference in New Issue
Block a user