Serialize query plan operators

Summary:
With the added support for serialization, we should be able to transfer
plans across distributed workers.

The planner tests has been extended to test serialization. Operators
should be mostly tested, but the expression they contain aren't
completely. The quick solution is to use typeid for rudimentary
expression equality testing.  The more involved solution of comparing
the expression tree for equality is the correct choice. It should be
done in the near future.

Reviewers: florijan, msantl

Reviewed By: florijan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1122
This commit is contained in:
Teon Banek 2018-01-22 10:27:00 +01:00
parent 2a8c64882f
commit 252018ab22
7 changed files with 1059 additions and 354 deletions

View File

@ -7,6 +7,18 @@
namespace query {
// Id for boost's archive get_helper needs to be unique among all ids. If it
// isn't unique, then different instances (even across different types!) will
// replace the previous helper. The documentation recommends to take an address
// of a function, which should be unique in the produced binary.
// It might seem logical to take an address of a AstTreeStorage constructor, but
// according to c++ standard, the constructor function need not have an address.
// Additionally, pointers to member functions are not required to contain the
// address of the function
// (https://isocpp.org/wiki/faq/pointers-to-members#addr-of-memfn). So, to be
// safe, use a regular top-level function.
void *const AstTreeStorage::kHelperId = (void *)CloneReturnBody;
AstTreeStorage::AstTreeStorage() {
storage_.emplace_back(new Query(next_uid_++));
}
@ -15,14 +27,6 @@ Query *AstTreeStorage::query() const {
return dynamic_cast<Query *>(storage_[0].get());
}
int AstTreeStorage::MaximumStorageUid() const {
int max_uid = -1;
for (const auto &tree : storage_) {
max_uid = std::max(max_uid, tree->uid());
}
return max_uid;
}
ReturnBody CloneReturnBody(AstTreeStorage &storage, const ReturnBody &body) {
ReturnBody new_body;
new_body.distinct = body.distinct;

View File

@ -1,6 +1,5 @@
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>
@ -9,13 +8,10 @@
#include "boost/serialization/split_member.hpp"
#include "boost/serialization/string.hpp"
#include "boost/serialization/vector.hpp"
#include "glog/logging.h"
#include "database/graph_db.hpp"
#include "query/frontend/ast/ast_visitor.hpp"
#include "query/frontend/semantic/symbol.hpp"
#include "query/interpret/awesome_memgraph_functions.hpp"
#include "query/parameters.hpp"
#include "query/typed_value.hpp"
#include "storage/types.hpp"
#include "utils/serialization.hpp"
@ -35,6 +31,10 @@ struct hash<std::pair<std::string, storage::Property>> {
};
} // namespace std
namespace database {
class GraphDbAccessor;
}
namespace query {
#define CLONE_BINARY_EXPRESSION \
@ -60,6 +60,7 @@ namespace query {
class Tree;
// It would be better to call this AstTree, but we already have a class Tree,
// which could be renamed to Node or AstTreeNode, but we also have a class
// called NodeAtom...
@ -82,14 +83,16 @@ class AstTreeStorage {
Query *query() const;
/// Id for using get_helper<AstTreeStorage> in boost archives.
static void * const kHelperId;
/// Load an Ast Node into this storage.
template <class TArchive, class TNode>
void Load(TArchive &ar, TNode &node) {
auto &tmp_ast = ar.template get_helper<AstTreeStorage>();
tmp_ast.storage_ = std::move(storage_);
auto &tmp_ast = ar.template get_helper<AstTreeStorage>(kHelperId);
std::swap(*this, tmp_ast);
ar >> node;
storage_ = std::move(tmp_ast.storage_);
next_uid_ = MaximumStorageUid() + 1;
std::swap(*this, tmp_ast);
}
/// Load a Query into this storage.
@ -102,8 +105,6 @@ class AstTreeStorage {
int next_uid_ = 0;
std::vector<std::unique_ptr<Tree>> storage_;
int MaximumStorageUid() const;
template <class TArchive, class TNode>
friend void LoadPointer(TArchive &ar, TNode *&node);
};
@ -117,7 +118,8 @@ template <class TArchive, class TNode>
void LoadPointer(TArchive &ar, TNode *&node) {
ar >> node;
if (node) {
auto &ast_storage = ar.template get_helper<AstTreeStorage>();
auto &ast_storage =
ar.template get_helper<AstTreeStorage>(AstTreeStorage::kHelperId);
auto found =
std::find_if(ast_storage.storage_.begin(), ast_storage.storage_.end(),
[&](const auto &n) { return n->uid() == node->uid(); });
@ -127,6 +129,7 @@ void LoadPointer(TArchive &ar, TNode *&node) {
dynamic_cast<TNode *>(found->get()) == node);
if (ast_storage.storage_.end() == found) {
ast_storage.storage_.emplace_back(node);
ast_storage.next_uid_ = std::max(ast_storage.next_uid_, node->uid() + 1);
}
}
}

View File

@ -2,6 +2,9 @@
#include <string>
#include "boost/serialization/serialization.hpp"
#include "boost/serialization/string.hpp"
namespace query {
class Symbol {

View File

@ -11,6 +11,7 @@
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/interpret/frame.hpp"
#include "query/parameters.hpp"
#include "query/typed_value.hpp"
#include "utils/exceptions.hpp"

View File

@ -1,16 +1,23 @@
#include "query/plan/operator.hpp"
#include <algorithm>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/export.hpp"
#include "glog/logging.h"
#include "database/graph_db_accessor.hpp"
#include "query/context.hpp"
#include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/interpret/eval.hpp"
#include "query/plan/operator.hpp"
#include "query/path.hpp"
// macro for the default implementation of LogicalOperator::Accept
// that accepts the visitor and visits it's input_ operator
@ -79,7 +86,7 @@ std::unique_ptr<Cursor> Once::MakeCursor(database::GraphDbAccessor &) const {
void Once::OnceCursor::Reset() { did_pull_ = false; }
CreateNode::CreateNode(const NodeAtom *node_atom,
CreateNode::CreateNode(NodeAtom *node_atom,
const std::shared_ptr<LogicalOperator> &input)
: node_atom_(node_atom), input_(input ? input : std::make_shared<Once>()) {}
@ -117,7 +124,7 @@ void CreateNode::CreateNodeCursor::Create(Frame &frame, Context &context) {
frame[context.symbol_table_.at(*self_.node_atom_->identifier_)] = new_node;
}
CreateExpand::CreateExpand(const NodeAtom *node_atom, const EdgeAtom *edge_atom,
CreateExpand::CreateExpand(NodeAtom *node_atom, EdgeAtom *edge_atom,
const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node)
: node_atom_(node_atom),
@ -2546,3 +2553,35 @@ void Union::UnionCursor::Reset() {
}
} // namespace query::plan
BOOST_CLASS_EXPORT(query::plan::Once);
BOOST_CLASS_EXPORT(query::plan::CreateNode);
BOOST_CLASS_EXPORT(query::plan::CreateExpand);
BOOST_CLASS_EXPORT(query::plan::ScanAll);
BOOST_CLASS_EXPORT(query::plan::ScanAllByLabel);
BOOST_CLASS_EXPORT(query::plan::ScanAllByLabelPropertyRange);
BOOST_CLASS_EXPORT(query::plan::ScanAllByLabelPropertyValue);
BOOST_CLASS_EXPORT(query::plan::Expand);
BOOST_CLASS_EXPORT(query::plan::ExpandVariable);
BOOST_CLASS_EXPORT(query::plan::Filter);
BOOST_CLASS_EXPORT(query::plan::Produce);
BOOST_CLASS_EXPORT(query::plan::ConstructNamedPath);
BOOST_CLASS_EXPORT(query::plan::Delete);
BOOST_CLASS_EXPORT(query::plan::SetProperty);
BOOST_CLASS_EXPORT(query::plan::SetProperties);
BOOST_CLASS_EXPORT(query::plan::SetLabels);
BOOST_CLASS_EXPORT(query::plan::RemoveProperty);
BOOST_CLASS_EXPORT(query::plan::RemoveLabels);
BOOST_CLASS_EXPORT(query::plan::ExpandUniquenessFilter<EdgeAccessor>);
BOOST_CLASS_EXPORT(query::plan::ExpandUniquenessFilter<VertexAccessor>);
BOOST_CLASS_EXPORT(query::plan::Accumulate);
BOOST_CLASS_EXPORT(query::plan::Aggregate);
BOOST_CLASS_EXPORT(query::plan::Skip);
BOOST_CLASS_EXPORT(query::plan::Limit);
BOOST_CLASS_EXPORT(query::plan::OrderBy);
BOOST_CLASS_EXPORT(query::plan::Merge);
BOOST_CLASS_EXPORT(query::plan::Optional);
BOOST_CLASS_EXPORT(query::plan::Unwind);
BOOST_CLASS_EXPORT(query::plan::Distinct);
BOOST_CLASS_EXPORT(query::plan::CreateIndex);
BOOST_CLASS_EXPORT(query::plan::Union);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff