Migrate labels/properties/edgetypes to ids

Summary:
In preparation for distributed storage we need to have labels/properties/edgetypes uniquely identifiable by their ids, which will be global in near future.
The old design has to be abandoned because it's not possible to keep track of global labels/properties/edgetypes while they are local pointers.

Reviewers: mislav.bradac, florijan

Reviewed By: florijan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D993
This commit is contained in:
Dominik Gleich 2017-11-23 16:36:54 +01:00
parent 5ef2d20133
commit 67538aceeb
17 changed files with 330 additions and 104 deletions

View File

@ -11,6 +11,7 @@
#include "database/indexes/label_property_index.hpp"
#include "durability/wal.hpp"
#include "mvcc/version_list.hpp"
#include "storage/concurrent_id_mapper.hpp"
#include "storage/deferred_deleter.hpp"
#include "storage/edge.hpp"
#include "storage/garbage_collector.hpp"
@ -114,9 +115,15 @@ class GraphDb {
// unique object stores
// TODO this should be also garbage collected
ConcurrentSet<std::string> labels_;
ConcurrentSet<std::string> edge_types_;
ConcurrentSet<std::string> properties_;
ConcurrentIdMapper<GraphDbTypes::Label, GraphDbTypes::Label::StorageT,
std::string>
labels_;
ConcurrentIdMapper<GraphDbTypes::EdgeType, GraphDbTypes::EdgeType::StorageT,
std::string>
edge_types_;
ConcurrentIdMapper<GraphDbTypes::Property, GraphDbTypes::Property::StorageT,
std::string>
properties_;
// indexes
KeyIndex<GraphDbTypes::Label, Vertex> labels_index_;

View File

@ -336,37 +336,37 @@ void GraphDbAccessor::RemoveEdge(EdgeAccessor &edge_accessor,
GraphDbTypes::Label GraphDbAccessor::Label(const std::string &label_name) {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return &(*db_.labels_.access().insert(label_name).first);
return db_.labels_.insert_value(label_name);
}
const std::string &GraphDbAccessor::LabelName(
const GraphDbTypes::Label label) const {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return *label;
return db_.labels_.value_by_id(label);
}
GraphDbTypes::EdgeType GraphDbAccessor::EdgeType(
const std::string &edge_type_name) {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return &(*db_.edge_types_.access().insert(edge_type_name).first);
return db_.edge_types_.insert_value(edge_type_name);
}
const std::string &GraphDbAccessor::EdgeTypeName(
const GraphDbTypes::EdgeType edge_type) const {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return *edge_type;
return db_.edge_types_.value_by_id(edge_type);
}
GraphDbTypes::Property GraphDbAccessor::Property(
const std::string &property_name) {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return &(*db_.properties_.access().insert(property_name).first);
return db_.properties_.insert_value(property_name);
}
const std::string &GraphDbAccessor::PropertyName(
const GraphDbTypes::Property property) const {
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
return *property;
return db_.properties_.value_by_id(property);
}
int64_t GraphDbAccessor::Counter(const std::string &name) {

View File

@ -2,10 +2,54 @@
#include <string>
#include "utils/total_ordering.hpp"
namespace GraphDbTypes {
// definitions for what data types are used for a Label, Property, EdgeType
// TODO: Typedefing pointers is terrible astractions, get rid of it.
using Label = const std::string *;
using EdgeType = const std::string *;
using Property = const std::string *;
template <typename TSpecificType>
class Common : TotalOrdering<TSpecificType> {
public:
using StorageT = uint16_t;
Common() {}
explicit Common(const StorageT storage) : storage_(storage) {}
friend bool operator==(const TSpecificType &a, const TSpecificType &b) {
return a.storage_ == b.storage_;
}
friend bool operator<(const TSpecificType &a, const TSpecificType &b) {
return a.storage_ < b.storage_;
}
struct Hash {
std::hash<StorageT> hash{};
size_t operator()(const TSpecificType &t) const { return hash(t.storage_); }
};
private:
StorageT storage_{0};
};
class Label : public Common<Label> {
using Common::Common;
};
class EdgeType : public Common<EdgeType> {
using Common::Common;
};
class Property : public Common<Property> {
using Common::Common;
};
}; // namespace GraphDbTypes
namespace std {
template <>
struct hash<GraphDbTypes::Label>
: public GraphDbTypes::Common<GraphDbTypes::Label>::Hash {};
template <>
struct hash<GraphDbTypes::EdgeType>
: public GraphDbTypes::Common<GraphDbTypes::EdgeType>::Hash {};
template <>
struct hash<GraphDbTypes::Property>
: public GraphDbTypes::Common<GraphDbTypes::Property>::Hash {};
}; // namespace std

View File

@ -1,6 +1,11 @@
/// @file
#pragma once
#include <experimental/optional>
#include "database/graph_db_datatypes.hpp"
#include "storage/property_value.hpp"
#include "utils/bound.hpp"
#include "utils/hashing/fnv.hpp"
namespace query::plan {
@ -129,4 +134,4 @@ auto MakeVertexCountCache(const TDbAccessor &db) {
return VertexCountCache<TDbAccessor>(db);
}
} // namespace plan::query
} // namespace query::plan

View File

@ -0,0 +1,57 @@
#pragma once
#include "glog/logging.h"
#include "data_structures/concurrent/concurrent_map.hpp"
/**
* @brief Implements injection between values and ids. Safe to use
* concurrently.
* @TParam TIds - Id type which to use
* @TParam TIdPrimitive - Primitive type which defines storage size (uint16_t,
* uint32_t, etc.)
* @TParam TRecord - Value type for which to define bijection
*/
template <typename TId, typename TIdPrimitive, typename TValue>
class ConcurrentIdMapper {
public:
/**
* Thread safe insert value and get a unique id for it. Calling this method
* with the same value from any number of threads will always return the same
* id, i.e. mapping between values and ids will always be consistent.
*/
TId insert_value(const TValue &value) {
auto value_to_id_acc = value_to_id_.access();
auto found = value_to_id_acc.find(value);
TId inserted_id(0);
if (found == value_to_id_acc.end()) {
TIdPrimitive new_id = id_.fetch_add(1);
DCHECK(new_id < std::numeric_limits<TIdPrimitive>::max())
<< "Number of used ids overflowed our container";
auto insert_result = value_to_id_acc.insert(value, TId(new_id));
// After we tried to insert value with our id we either got our id, or the
// id created by the thread which succesfully inserted (value, id) pair
inserted_id = insert_result.first->second;
} else {
inserted_id = found->second;
}
auto id_to_value_acc = id_to_value_.access();
// We have to try to insert the inserted_id and value even if we are not the
// one who assigned id because we have to make sure that after this method
// returns that both mappings between id->value and value->id exist.
id_to_value_acc.insert(inserted_id, value);
return inserted_id;
}
const TValue &value_by_id(const TId &id) const {
const auto id_to_value_acc = id_to_value_.access();
auto result = id_to_value_acc.find(id);
DCHECK(result != id_to_value_acc.end());
return result->second;
}
private:
ConcurrentMap<TId, TValue> id_to_value_;
ConcurrentMap<TValue, TId> value_to_id_;
std::atomic<TIdPrimitive> id_{0};
};

View File

@ -90,9 +90,10 @@ class Edges {
* present in this iterator. */
void update_position() {
if (vertex_.local()) {
position_ = std::find_if(
position_, end_,
[v = this->vertex_](const Element &e) { return e.vertex == v; });
position_ = std::find_if(position_,
end_, [v = this->vertex_](const Element &e) {
return e.vertex == v;
});
}
if (edge_types_) {
position_ = std::find_if(position_, end_, [this](const Element &e) {

View File

@ -14,8 +14,7 @@ class Timestamp : public TotalOrdering<Timestamp> {
public:
Timestamp() : Timestamp(0, 0) {}
Timestamp(std::time_t time, long nsec = 0)
: unix_time(time), nsec(nsec) {
Timestamp(std::time_t time, long nsec = 0) : unix_time(time), nsec(nsec) {
auto result = gmtime_r(&time, &this->time);
if (result == nullptr)
@ -51,9 +50,9 @@ class Timestamp : public TotalOrdering<Timestamp> {
subsec());
}
const std::string to_string(const std::string &format = fiso8601) const {
const std::string to_string(const std::string& format = fiso8601) const {
return fmt::format(format, year(), month(), day(), hour(), min(), sec(),
subsec());
subsec());
}
friend std::ostream& operator<<(std::ostream& stream, const Timestamp& ts) {

View File

@ -132,33 +132,39 @@ class Timer {
// Dummy DbAccessor which forwards user input for various vertex counts.
class InteractiveDbAccessor {
public:
InteractiveDbAccessor(int64_t vertices_count, Timer &timer)
: vertices_count_(vertices_count), timer_(timer) {}
InteractiveDbAccessor(GraphDbAccessor &dba, int64_t vertices_count,
Timer &timer)
: dba_(dba), vertices_count_(vertices_count), timer_(timer) {}
int64_t VerticesCount() const { return vertices_count_; }
int64_t VerticesCount(const GraphDbTypes::Label &label) const {
if (label_vertex_count_.find(*label) == label_vertex_count_.end()) {
label_vertex_count_[*label] = ReadVertexCount("label '" + *label + "'");
int64_t VerticesCount(const GraphDbTypes::Label &label_id) const {
auto label = dba_.LabelName(label_id);
if (label_vertex_count_.find(label) == label_vertex_count_.end()) {
label_vertex_count_[label] = ReadVertexCount("label '" + label + "'");
}
return label_vertex_count_.at(*label);
return label_vertex_count_.at(label);
}
int64_t VerticesCount(const GraphDbTypes::Label &label,
const GraphDbTypes::Property &property) const {
auto key = std::make_pair(*label, *property);
int64_t VerticesCount(const GraphDbTypes::Label &label_id,
const GraphDbTypes::Property &property_id) const {
auto label = dba_.LabelName(label_id);
auto property = dba_.PropertyName(property_id);
auto key = std::make_pair(label, property);
if (label_property_vertex_count_.find(key) ==
label_property_vertex_count_.end()) {
label_property_vertex_count_[key] = ReadVertexCount(
"label '" + *label + "' and property '" + *property + "'");
"label '" + label + "' and property '" + property + "'");
}
return label_property_vertex_count_.at(key);
}
int64_t VerticesCount(const GraphDbTypes::Label &label,
const GraphDbTypes::Property &property,
int64_t VerticesCount(const GraphDbTypes::Label &label_id,
const GraphDbTypes::Property &property_id,
const PropertyValue &value) const {
auto label_prop = std::make_pair(*label, *property);
auto label = dba_.LabelName(label_id);
auto property = dba_.PropertyName(property_id);
auto label_prop = std::make_pair(label, property);
if (label_property_index_.find(label_prop) == label_property_index_.end()) {
return 0;
}
@ -166,18 +172,21 @@ class InteractiveDbAccessor {
if (value_vertex_count.find(value) == value_vertex_count.end()) {
std::stringstream ss;
ss << value;
int64_t count = ReadVertexCount("label '" + *label + "' and property '" +
*property + "' value '" + ss.str() + "'");
int64_t count = ReadVertexCount("label '" + label + "' and property '" +
property + "' value '" + ss.str() + "'");
value_vertex_count[value] = count;
}
return value_vertex_count.at(value);
}
int64_t VerticesCount(
const GraphDbTypes::Label &label, const GraphDbTypes::Property &property,
const GraphDbTypes::Label &label_id,
const GraphDbTypes::Property &property_id,
const std::experimental::optional<utils::Bound<PropertyValue>> lower,
const std::experimental::optional<utils::Bound<PropertyValue>> upper)
const {
auto label = dba_.LabelName(label_id);
auto property = dba_.PropertyName(property_id);
std::stringstream range_string;
if (lower) {
range_string << (lower->IsInclusive() ? "[" : "(") << lower->value()
@ -188,17 +197,19 @@ class InteractiveDbAccessor {
if (upper) {
range_string << upper->value() << (upper->IsInclusive() ? "]" : ")");
}
return ReadVertexCount("label '" + *label + "' and property '" + *property +
return ReadVertexCount("label '" + label + "' and property '" + property +
"' in range " + range_string.str());
}
bool LabelPropertyIndexExists(const GraphDbTypes::Label &label,
const GraphDbTypes::Property &property) const {
auto key = std::make_pair(*label, *property);
bool LabelPropertyIndexExists(
const GraphDbTypes::Label &label_id,
const GraphDbTypes::Property &property_id) const {
auto label = dba_.LabelName(label_id);
auto property = dba_.PropertyName(property_id);
auto key = std::make_pair(label, property);
if (label_property_index_.find(key) == label_property_index_.end()) {
bool resp = timer_.WithPause([&label, &property]() {
return AskYesNo("Index for ':" + *label + "(" + *property +
")' exists:");
return AskYesNo("Index for ':" + label + "(" + property + ")' exists:");
});
label_property_index_[key] = resp;
}
@ -307,6 +318,7 @@ class InteractiveDbAccessor {
private:
typedef std::pair<std::string, std::string> LabelPropertyKey;
GraphDbAccessor &dba_;
int64_t vertices_count_;
Timer &timer_;
mutable std::map<std::string, int64_t> label_vertex_count_;
@ -352,9 +364,11 @@ class InteractiveDbAccessor {
class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
public:
using HierarchicalLogicalOperatorVisitor::PostVisit;
using HierarchicalLogicalOperatorVisitor::PreVisit;
using HierarchicalLogicalOperatorVisitor::Visit;
using HierarchicalLogicalOperatorVisitor::PostVisit;
explicit PlanPrinter(GraphDbAccessor &dba) : dba_(dba) {}
#define PRE_VISIT(TOp) \
bool PreVisit(query::plan::TOp &) override { \
@ -377,7 +391,8 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
bool PreVisit(query::plan::ScanAllByLabel &op) override {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabel"
<< " (" << op.output_symbol().name() << " :" << *op.label() << ")";
<< " (" << op.output_symbol().name() << " :"
<< dba_.LabelName(op.label()) << ")";
});
return true;
}
@ -385,8 +400,9 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
bool PreVisit(query::plan::ScanAllByLabelPropertyValue &op) override {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabelPropertyValue"
<< " (" << op.output_symbol().name() << " :" << *op.label() << " {"
<< *op.property() << "})";
<< " (" << op.output_symbol().name() << " :"
<< dba_.LabelName(op.label()) << " {"
<< dba_.PropertyName(op.property()) << "})";
});
return true;
}
@ -394,8 +410,9 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
bool PreVisit(query::plan::ScanAllByLabelPropertyRange &op) override {
WithPrintLn([&](auto &out) {
out << "* ScanAllByLabelPropertyRange"
<< " (" << op.output_symbol().name() << " :" << *op.label() << " {"
<< *op.property() << "})";
<< " (" << op.output_symbol().name() << " :"
<< dba_.LabelName(op.label()) << " {"
<< dba_.PropertyName(op.property()) << "})";
});
return true;
}
@ -500,6 +517,7 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
}
int depth_ = 0;
GraphDbAccessor &dba_;
};
// Shorthand for a vector of pairs (logical_plan, cost).
@ -511,22 +529,24 @@ typedef std::vector<
struct Command {
typedef std::vector<std::string> Args;
// Function of this command
std::function<void(PlansWithCost &, const Args &)> function;
std::function<void(GraphDbAccessor &, PlansWithCost &, const Args &)>
function;
// Number of arguments the function works with.
int arg_count;
// Explanation of the command.
std::string documentation;
};
#define DEFCOMMAND(Name) \
void Name##Command(PlansWithCost &plans, const Command::Args &args)
#define DEFCOMMAND(Name) \
void Name##Command(GraphDbAccessor &dba, PlansWithCost &plans, \
const Command::Args &args)
DEFCOMMAND(Top) {
int64_t n_plans = 0;
std::stringstream ss(args[0]);
ss >> n_plans;
if (ss.fail() || !ss.eof()) return;
PlanPrinter printer;
PlanPrinter printer(dba);
n_plans = std::min(static_cast<int64_t>(plans.size()), n_plans);
for (int64_t i = 0; i < n_plans; ++i) {
auto &plan_pair = plans[i];
@ -545,7 +565,7 @@ DEFCOMMAND(Show) {
const auto &plan = plans[plan_ix].first;
auto cost = plans[plan_ix].second;
std::cout << "Plan cost: " << cost << std::endl;
PlanPrinter printer;
PlanPrinter printer(dba);
plan->Accept(printer);
}
@ -571,6 +591,7 @@ DEFCOMMAND(Help) {
#undef DEFCOMMAND
void ExaminePlans(
GraphDbAccessor &dba,
std::vector<std::pair<std::unique_ptr<query::plan::LogicalOperator>,
double>> &plans) {
while (true) {
@ -592,7 +613,7 @@ void ExaminePlans(
<< " arguments" << std::endl;
continue;
}
command.function(plans, args);
command.function(dba, plans, args);
}
}
@ -646,9 +667,11 @@ int main(int argc, char *argv[]) {
std::exit(EXIT_FAILURE);
}
GraphDb db;
GraphDbAccessor dba(db);
Timer planning_timer;
InteractiveDbAccessor interactive_db(
in_db_filename.empty() ? ReadInt("Vertices in DB: ") : 0, planning_timer);
dba, in_db_filename.empty() ? ReadInt("Vertices in DB: ") : 0,
planning_timer);
if (!in_db_filename.empty()) {
std::ifstream db_file(in_db_filename);
interactive_db.Load(db_file);
@ -658,7 +681,6 @@ int main(int argc, char *argv[]) {
if (!line || *line == "quit") break;
if (line->empty()) continue;
try {
GraphDbAccessor dba(db);
auto ast = MakeAst(*line, dba);
auto symbol_table = MakeSymbolTable(ast);
planning_timer.Start();
@ -669,7 +691,7 @@ int main(int argc, char *argv[]) {
<< std::chrono::duration<double, std::milli>(planning_time).count()
<< "ms" << std::endl;
std::cout << "Generated " << plans.size() << " plans" << std::endl;
ExaminePlans(plans);
ExaminePlans(dba, plans);
} catch (const utils::BasicException &e) {
std::cout << "Error: " << e.what() << std::endl;
}

View File

@ -54,12 +54,12 @@ RC_GTEST_PROP(RandomGraph, RandomGraph,
for (const auto &vertex : dba.Vertices(false)) {
auto label = vertex_label_map.at(vertex);
RC_ASSERT(vertex.labels().size() == 1);
RC_ASSERT(*vertex.labels()[0] == label);
RC_ASSERT(dba.LabelName(vertex.labels()[0]) == label);
vertices_num_check++;
}
for (const auto &edge : dba.Edges(false)) {
auto type = edge_type_map.at(edge);
RC_ASSERT(*edge.EdgeType() == type);
RC_ASSERT(dba.EdgeTypeName(edge.EdgeType()) == type);
edges_num_check++;
}
RC_ASSERT(vertices_num_check == vertices_num);

View File

@ -168,21 +168,24 @@ TEST(BoltEncoder, VertexAndEdge) {
GraphDbAccessor db_accessor(db);
auto va1 = db_accessor.InsertVertex();
auto va2 = db_accessor.InsertVertex();
std::string l1("label1"), l2("label2");
va1.add_label(&l1);
va1.add_label(&l2);
std::string p1("prop1"), p2("prop2");
auto l1 = db_accessor.Label("label1");
auto l2 = db_accessor.Label("label2");
va1.add_label(l1);
va1.add_label(l2);
auto p1 = db_accessor.Property("prop1");
auto p2 = db_accessor.Property("prop2");
PropertyValue pv1(12), pv2(200);
va1.PropsSet(&p1, pv1);
va1.PropsSet(&p2, pv2);
va1.PropsSet(p1, pv1);
va1.PropsSet(p2, pv2);
// create edge
std::string et("edgetype");
auto ea = db_accessor.InsertEdge(va1, va2, &et);
std::string p3("prop3"), p4("prop4");
auto et = db_accessor.EdgeType("edgetype");
auto ea = db_accessor.InsertEdge(va1, va2, et);
auto p3 = db_accessor.Property("prop3");
auto p4 = db_accessor.Property("prop4");
PropertyValue pv3(42), pv4(1234);
ea.PropsSet(&p3, pv3);
ea.PropsSet(&p4, pv4);
ea.PropsSet(p3, pv3);
ea.PropsSet(p4, pv4);
// check everything
std::vector<TypedValue> vals;

View File

@ -0,0 +1,74 @@
#include <thread>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "storage/concurrent_id_mapper.hpp"
const int THREAD_NUM = 20;
const int VALUE_MAX = 50;
TEST(ConcurrentIdMapper, SameValueGivesSameId) {
ConcurrentIdMapper<int, int, int> mapper;
EXPECT_EQ(mapper.insert_value(1), mapper.insert_value(1));
}
TEST(ConcurrentIdMapper, IdToValue) {
ConcurrentIdMapper<int, int, int> mapper;
auto value = 1;
auto id = mapper.insert_value(value);
EXPECT_EQ(value, mapper.value_by_id(id));
}
TEST(ConcurrentIdMapper, TwoValuesTwoIds) {
ConcurrentIdMapper<int, int, int> mapper;
EXPECT_NE(mapper.insert_value(1), mapper.insert_value(2));
}
TEST(ConcurrentIdMapper, SameIdReturnedMultipleThreads) {
std::vector<std::thread> threads;
ConcurrentIdMapper<int, int, int> mapper;
std::vector<std::vector<int>> thread_value_ids(THREAD_NUM);
std::atomic<int> current_value{0};
std::atomic<int> current_value_insertion_count{0};
// Try to insert every value from [0, VALUE_MAX] by multiple threads in the
// same time
for (int i = 0; i < THREAD_NUM; ++i) {
threads.push_back(std::thread([&mapper, &thread_value_ids, &current_value,
&current_value_insertion_count, i]() {
int last = -1;
while (current_value <= VALUE_MAX) {
while (last == current_value) continue;
auto id = mapper.insert_value(current_value.load());
thread_value_ids[i].push_back(id);
// Also check that reverse mapping exists after method exits
EXPECT_EQ(mapper.value_by_id(id), current_value.load());
last = current_value;
current_value_insertion_count.fetch_add(1);
}
}));
}
// Increment current_value when all threads finish inserting it and getting an
// id for it
threads.push_back(
std::thread([&current_value, &current_value_insertion_count]() {
while (current_value.load() <= VALUE_MAX) {
while (current_value_insertion_count.load() != THREAD_NUM) continue;
current_value_insertion_count.store(0);
current_value.fetch_add(1);
}
}));
for (auto &thread : threads) thread.join();
// For every value inserted, each thread should have the same id
for (int i = 0; i < THREAD_NUM; ++i)
for (int j = 0; j < THREAD_NUM; ++j)
EXPECT_EQ(thread_value_ids[i], thread_value_ids[j]);
// Each value should have a unique id
std::set<int> ids(thread_value_ids[0].begin(), thread_value_ids[0].end());
EXPECT_EQ(ids.size(), thread_value_ids[0].size());
}

View File

@ -164,15 +164,25 @@ void CompareDbs(GraphDb &a, GraphDb &b) {
<< utils::Join(index_b, ", ");
}
auto is_permutation_props = [&dba_a, &dba_b](const auto &p1, const auto p2) {
auto is_permutation_props = [&dba_a, &dba_b](const auto &p1_id,
const auto &p2_id) {
std::vector<std::pair<std::string, query::TypedValue>> p1;
std::vector<std::pair<std::string, query::TypedValue>> p2;
for (auto x : p1_id) p1.push_back({dba_a.PropertyName(x.first), x.second});
for (auto x : p2_id) p2.push_back({dba_b.PropertyName(x.first), x.second});
// Don't use a binary predicate which depends on different value getters
// semantics for two containers because is_permutation might call the
// predicate with both arguments on the same container
return p1.size() == p2.size() &&
std::is_permutation(
p1.begin(), p1.end(), p2.begin(),
[&dba_a, &dba_b](const auto &p1, const auto &p2) {
return dba_a.PropertyName(p1.first) ==
dba_b.PropertyName(p2.first) &&
query::TypedValue::BoolEqual{}(p1.second, p2.second);
});
std::is_permutation(p1.begin(), p1.end(), p2.begin(),
[](const auto &p1, const auto &p2) {
return p1.first == p2.first &&
query::TypedValue::BoolEqual{}(
p1.second, p2.second);
});
};
{
@ -182,11 +192,12 @@ void CompareDbs(GraphDb &a, GraphDb &b) {
auto v_b = dba_b.FindVertex(v_a.id(), false);
ASSERT_TRUE(v_b) << "Vertex not found, id: " << v_a.id();
ASSERT_EQ(v_a.labels().size(), v_b->labels().size());
EXPECT_TRUE(std::is_permutation(
v_a.labels().begin(), v_a.labels().end(), v_b->labels().begin(),
[&dba_a, &dba_b](const auto &la, const auto &lb) {
return dba_a.LabelName(la) == dba_b.LabelName(lb);
}));
std::vector<std::string> v_a_labels;
std::vector<std::string> v_b_labels;
for (auto x : v_a.labels()) v_a_labels.push_back(dba_a.LabelName(x));
for (auto x : v_b->labels()) v_b_labels.push_back(dba_b.LabelName(x));
EXPECT_TRUE(std::is_permutation(v_a_labels.begin(), v_a_labels.end(),
v_b_labels.begin()));
EXPECT_TRUE(is_permutation_props(v_a.Properties(), v_b->Properties()));
}
auto vertices_b = dba_b.Vertices(false);

View File

@ -38,7 +38,7 @@ TEST(GraphDbAccessorTest, UniqueVertexId) {
std::vector<std::thread> threads;
for (int i = 0; i < 50; i++) {
threads.emplace_back([&db, &ids]() {
GraphDbAccessor dba(db);
GraphDbAccessor dba(db);
auto access = ids.access();
for (int i = 0; i < 200; i++) access.insert(dba.InsertVertex().id());
});
@ -350,7 +350,7 @@ TEST(GraphDbAccessorTest, Properties) {
GraphDb db;
GraphDbAccessor dba(db);
GraphDbTypes::EdgeType prop = dba.Property("name");
GraphDbTypes::Property prop = dba.Property("name");
EXPECT_EQ(prop, dba.Property("name"));
EXPECT_NE(prop, dba.Property("surname"));
EXPECT_EQ(dba.PropertyName(prop), "name");

View File

@ -125,10 +125,10 @@ TEST_F(GraphDbAccessorIndex, LabelPropertyIndexBuildTwice) {
TEST_F(GraphDbAccessorIndex, LabelPropertyIndexCount) {
dba->BuildIndex(label, property);
EXPECT_EQ(dba->VerticesCount(label, property), 0);
EXPECT_EQ(Count(dba->Vertices(label, property)), 0);
EXPECT_EQ(Count(dba->Vertices(label, property, true)), 0);
for (int i = 0; i < 14; ++i) AddVertex(0);
EXPECT_EQ(dba->VerticesCount(label, property), 14);
EXPECT_EQ(Count(dba->Vertices(label, property)), 14);
EXPECT_EQ(Count(dba->Vertices(label, property, true)), 14);
}
TEST(GraphDbAccessorIndexApi, LabelPropertyBuildIndexConcurrent) {
@ -147,7 +147,8 @@ TEST(GraphDbAccessorIndexApi, LabelPropertyBuildIndexConcurrent) {
dba.Abort();
success = 0;
}
}).detach();
})
.detach();
};
int build_1_success = -1;

View File

@ -180,9 +180,10 @@ auto GetEdgeVariable(AstTreeStorage &storage, const std::string &name,
/// Name is used to create the Identifier which is assigned to the node.
///
auto GetNode(AstTreeStorage &storage, const std::string &name,
GraphDbTypes::Label label = nullptr) {
std::experimental::optional<GraphDbTypes::Label> label =
std::experimental::nullopt) {
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>(name));
if (label) node->labels_.emplace_back(label);
if (label) node->labels_.emplace_back(*label);
return node;
}

View File

@ -801,13 +801,14 @@ TEST(ExpressionEvaluator, FunctionProperties) {
}
return properties;
};
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {v1})),
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {v1}, db)),
UnorderedElementsAre(testing::Pair("height", 5),
testing::Pair("age", 10)));
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {e})),
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {e}, db)),
UnorderedElementsAre(testing::Pair("height", 3),
testing::Pair("age", 15)));
ASSERT_THROW(EvaluateFunction("PROPERTIES", {2}), QueryRuntimeException);
ASSERT_THROW(EvaluateFunction("PROPERTIES", {2}, db), QueryRuntimeException);
}
TEST(ExpressionEvaluator, FunctionLast) {
@ -935,8 +936,8 @@ TEST(ExpressionEvaluator, FunctionType) {
auto v2 = dba.InsertVertex();
v2.add_label(dba.Label("label2"));
auto e = dba.InsertEdge(v1, v2, dba.EdgeType("type1"));
ASSERT_EQ(EvaluateFunction("TYPE", {e}).Value<std::string>(), "type1");
ASSERT_THROW(EvaluateFunction("TYPE", {2}), QueryRuntimeException);
ASSERT_EQ(EvaluateFunction("TYPE", {e}, db).Value<std::string>(), "type1");
ASSERT_THROW(EvaluateFunction("TYPE", {2}, db), QueryRuntimeException);
}
TEST(ExpressionEvaluator, FunctionLabels) {
@ -950,12 +951,12 @@ TEST(ExpressionEvaluator, FunctionLabels) {
v.add_label(dba.Label("label2"));
std::vector<std::string> labels;
auto _labels =
EvaluateFunction("LABELS", {v}).Value<std::vector<TypedValue>>();
EvaluateFunction("LABELS", {v}, db).Value<std::vector<TypedValue>>();
for (auto label : _labels) {
labels.push_back(label.Value<std::string>());
}
ASSERT_THAT(labels, UnorderedElementsAre("label1", "label2"));
ASSERT_THROW(EvaluateFunction("LABELS", {2}), QueryRuntimeException);
ASSERT_THROW(EvaluateFunction("LABELS", {2}, db), QueryRuntimeException);
}
TEST(ExpressionEvaluator, FunctionNodesRelationships) {
@ -1040,11 +1041,11 @@ TEST(ExpressionEvaluator, FunctionKeys) {
}
return keys;
};
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {v1})),
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {v1}, db)),
UnorderedElementsAre("height", "age"));
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {e})),
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {e}, db)),
UnorderedElementsAre("width", "age"));
ASSERT_THROW(EvaluateFunction("KEYS", {2}), QueryRuntimeException);
ASSERT_THROW(EvaluateFunction("KEYS", {2}, db), QueryRuntimeException);
}
TEST(ExpressionEvaluator, FunctionTail) {

View File

@ -103,7 +103,7 @@ TEST(QueryPlan, CreateExpand) {
GraphDbTypes::Label label_node_1 = dba.Label("Node1");
GraphDbTypes::Label label_node_2 = dba.Label("Node2");
auto property = PROPERTY_PAIR("property");
GraphDbTypes::EdgeType edge_type = dba.Label("edge_type");
GraphDbTypes::EdgeType edge_type = dba.EdgeType("edge_type");
SymbolTable symbol_table;
AstTreeStorage storage;
@ -210,7 +210,7 @@ TEST(QueryPlan, MatchCreateExpand) {
// GraphDbTypes::Label label_node_1 = dba.Label("Node1");
// GraphDbTypes::Label label_node_2 = dba.Label("Node2");
// GraphDbTypes::Property property = dba.Label("prop");
GraphDbTypes::EdgeType edge_type = dba.Label("edge_type");
GraphDbTypes::EdgeType edge_type = dba.EdgeType("edge_type");
SymbolTable symbol_table;
AstTreeStorage storage;