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 { 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() { AstTreeStorage::AstTreeStorage() {
storage_.emplace_back(new Query(next_uid_++)); storage_.emplace_back(new Query(next_uid_++));
} }
@ -15,14 +27,6 @@ Query *AstTreeStorage::query() const {
return dynamic_cast<Query *>(storage_[0].get()); 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 CloneReturnBody(AstTreeStorage &storage, const ReturnBody &body) {
ReturnBody new_body; ReturnBody new_body;
new_body.distinct = body.distinct; new_body.distinct = body.distinct;

View File

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

View File

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

View File

@ -1,16 +1,23 @@
#include "query/plan/operator.hpp"
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/export.hpp"
#include "glog/logging.h" #include "glog/logging.h"
#include "database/graph_db_accessor.hpp"
#include "query/context.hpp" #include "query/context.hpp"
#include "query/exceptions.hpp" #include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp" #include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/interpret/eval.hpp" #include "query/interpret/eval.hpp"
#include "query/path.hpp"
#include "query/plan/operator.hpp"
// macro for the default implementation of LogicalOperator::Accept // macro for the default implementation of LogicalOperator::Accept
// that accepts the visitor and visits it's input_ operator // 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; } void Once::OnceCursor::Reset() { did_pull_ = false; }
CreateNode::CreateNode(const NodeAtom *node_atom, CreateNode::CreateNode(NodeAtom *node_atom,
const std::shared_ptr<LogicalOperator> &input) const std::shared_ptr<LogicalOperator> &input)
: node_atom_(node_atom), input_(input ? input : std::make_shared<Once>()) {} : 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; 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, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node) Symbol input_symbol, bool existing_node)
: node_atom_(node_atom), : node_atom_(node_atom),
@ -2546,3 +2553,35 @@ void Union::UnionCursor::Reset() {
} }
} // namespace query::plan } // 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