Allow planning Cartesian after Produce
Summary: Hopefully, the mechanism of generating Cartesian is general enough, so this simple change should work correctly in all cases. Planner tests have been modified to use a FakeDbAccessor in order to speed them up and potentially allow extracting planning into a library. Reviewers: msantl, mtomic, buda Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1431
This commit is contained in:
parent
64f189cc8a
commit
4b97747c14
@ -219,6 +219,163 @@ class IndependentSubtreeFinder : public HierarchicalLogicalOperatorVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Optional &optional) override {
|
||||||
|
throw utils::NotYetImplemented("distributed Cartesian planning");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Unwind &unwind) override {
|
||||||
|
if (subtree_) return true;
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
unwind.input_expression()->Accept(collector);
|
||||||
|
if (auto found = ContainsForbidden(collector.symbols_)) {
|
||||||
|
subtree_ = unwind.input();
|
||||||
|
parent_ = &unwind;
|
||||||
|
depends_on_ = found;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining operators can only appear if they don't contain any forbidden
|
||||||
|
// symbols. This is the case when we are planning *another* Cartesian after
|
||||||
|
// Produce.
|
||||||
|
|
||||||
|
bool PostVisit(CreateNode &op) override {
|
||||||
|
CHECK(!FindForbidden(symbol_table_->at(*(op.node_atom()->identifier_))));
|
||||||
|
for (auto &kv : op.node_atom()->properties_) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
kv.second->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(CreateExpand &op) override {
|
||||||
|
CHECK(!FindForbidden(op.input_symbol()));
|
||||||
|
CHECK(!FindForbidden(symbol_table_->at(*(op.node_atom()->identifier_))));
|
||||||
|
CHECK(!FindForbidden(symbol_table_->at(*(op.edge_atom()->identifier_))));
|
||||||
|
for (auto &kv : op.node_atom()->properties_) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
kv.second->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
for (auto &kv : op.edge_atom()->properties_) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
kv.second->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Delete &op) override {
|
||||||
|
for (auto *expr : op.expressions()) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
expr->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(SetProperty &op) override {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
op.lhs()->Accept(collector);
|
||||||
|
op.rhs()->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(SetProperties &op) override {
|
||||||
|
CHECK(!FindForbidden(op.input_symbol()));
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
op.rhs()->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(SetLabels &op) override {
|
||||||
|
CHECK(!FindForbidden(op.input_symbol()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(RemoveProperty &op) override {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
op.lhs()->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(RemoveLabels &op) override {
|
||||||
|
CHECK(!FindForbidden(op.input_symbol()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Aggregate &aggr) override {
|
||||||
|
CHECK(!ContainsForbidden(aggr.remember()));
|
||||||
|
for (auto &elem : aggr.aggregations()) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
if (elem.value) elem.value->Accept(collector);
|
||||||
|
if (elem.key) elem.key->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
for (auto *expr : aggr.group_by()) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
expr->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Skip &skip) override {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
skip.expression()->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Limit &limit) override {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
limit.expression()->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(OrderBy &order_by) override {
|
||||||
|
CHECK(!ContainsForbidden(order_by.output_symbols()));
|
||||||
|
for (auto *expr : order_by.order_by()) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
expr->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Distinct &distinct) override {
|
||||||
|
CHECK(!ContainsForbidden(distinct.value_symbols()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Cartesian &cart) override {
|
||||||
|
CHECK(!ContainsForbidden(cart.left_symbols()) &&
|
||||||
|
!ContainsForbidden(cart.right_symbols()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(Synchronize &op) override { return true; }
|
||||||
|
|
||||||
|
bool PostVisit(PullRemote &pull) override {
|
||||||
|
CHECK(!ContainsForbidden(pull.symbols()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostVisit(PullRemoteOrderBy &pull) override {
|
||||||
|
CHECK(!ContainsForbidden(pull.symbols()));
|
||||||
|
for (auto *expr : pull.order_by()) {
|
||||||
|
UsedSymbolsCollector collector(*symbol_table_);
|
||||||
|
expr->Accept(collector);
|
||||||
|
CHECK(!ContainsForbidden(collector.symbols_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Independent subtree
|
// Independent subtree
|
||||||
std::shared_ptr<LogicalOperator> subtree_;
|
std::shared_ptr<LogicalOperator> subtree_;
|
||||||
// Immediate parent of `subtree_`.
|
// Immediate parent of `subtree_`.
|
||||||
@ -231,6 +388,10 @@ class IndependentSubtreeFinder : public HierarchicalLogicalOperatorVisitor {
|
|||||||
throw utils::NotYetImplemented("distributed Cartesian planning");
|
throw utils::NotYetImplemented("distributed Cartesian planning");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::vector<Symbol>> forbidden_symbols_;
|
||||||
|
const SymbolTable *symbol_table_;
|
||||||
|
|
||||||
template <class TCollection>
|
template <class TCollection>
|
||||||
std::experimental::optional<int64_t> ContainsForbidden(
|
std::experimental::optional<int64_t> ContainsForbidden(
|
||||||
const TCollection &symbols) {
|
const TCollection &symbols) {
|
||||||
@ -252,10 +413,6 @@ class IndependentSubtreeFinder : public HierarchicalLogicalOperatorVisitor {
|
|||||||
}
|
}
|
||||||
return std::experimental::nullopt;
|
return std::experimental::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::vector<Symbol>> forbidden_symbols_;
|
|
||||||
const SymbolTable *symbol_table_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the subtree parent, below which no operator uses symbols found in the
|
// Find the subtree parent, below which no operator uses symbols found in the
|
||||||
@ -788,13 +945,7 @@ class DistributedPlanner : public HierarchicalLogicalOperatorVisitor {
|
|||||||
// TODO: It might be better to plan Cartesians later if this Produce isn't
|
// TODO: It might be better to plan Cartesians later if this Produce isn't
|
||||||
// the last one and is not followed by an operator which requires a merge
|
// the last one and is not followed by an operator which requires a merge
|
||||||
// point (Skip, OrderBy, etc.).
|
// point (Skip, OrderBy, etc.).
|
||||||
if (!on_master_) {
|
Split(produce, PlanCartesian(produce.input()));
|
||||||
Split(produce, PlanCartesian(produce.input()));
|
|
||||||
} else {
|
|
||||||
// We are on master, so our produce input must come on the left hand
|
|
||||||
// side.
|
|
||||||
throw utils::NotYetImplemented("distributed planning");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -992,6 +1143,11 @@ class DistributedPlanner : public HierarchicalLogicalOperatorVisitor {
|
|||||||
// which contains that dependency.
|
// which contains that dependency.
|
||||||
branch.parent_start = input;
|
branch.parent_start = input;
|
||||||
}
|
}
|
||||||
|
if (on_master_ && cartesian_branches_.empty()) {
|
||||||
|
// Since we are planning a new Cartesian, the first CartesianBranch must
|
||||||
|
// not be sent to workers if we are executing on master.
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
// Send the independent subtree to workers and wire it in PullRemote
|
// Send the independent subtree to workers and wire it in PullRemote
|
||||||
auto id = AddWorkerPlan(branch.subtree);
|
auto id = AddWorkerPlan(branch.subtree);
|
||||||
branch.subtree = std::make_shared<PullRemote>(
|
branch.subtree = std::make_shared<PullRemote>(
|
||||||
@ -1018,7 +1174,6 @@ class DistributedPlanner : public HierarchicalLogicalOperatorVisitor {
|
|||||||
// Sets the master_op input to be merge_op. Afterwards, on_master_ is true.
|
// Sets the master_op input to be merge_op. Afterwards, on_master_ is true.
|
||||||
template <class TOp>
|
template <class TOp>
|
||||||
void Split(TOp &master_op, std::shared_ptr<LogicalOperator> merge_op) {
|
void Split(TOp &master_op, std::shared_ptr<LogicalOperator> merge_op) {
|
||||||
if (on_master_) throw utils::NotYetImplemented("distributed planning");
|
|
||||||
on_master_ = true;
|
on_master_ = true;
|
||||||
master_op.set_input(merge_op);
|
master_op.set_input(merge_op);
|
||||||
}
|
}
|
||||||
|
@ -3142,7 +3142,9 @@ std::vector<Symbol> Synchronize::ModifiedSymbols(
|
|||||||
|
|
||||||
bool Synchronize::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
|
bool Synchronize::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
|
||||||
if (visitor.PreVisit(*this)) {
|
if (visitor.PreVisit(*this)) {
|
||||||
input_->Accept(visitor) && pull_remote_->Accept(visitor);
|
// pull_remote_ is optional here, so visit it only if we continue visiting
|
||||||
|
// and pull_remote_ does exist.
|
||||||
|
input_->Accept(visitor) && pull_remote_ && pull_remote_->Accept(visitor);
|
||||||
}
|
}
|
||||||
return visitor.PostVisit(*this);
|
return visitor.PostVisit(*this);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ and false on every following Pull.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(node-atom "NodeAtom *" :initval "nullptr"
|
(node-atom "NodeAtom *" :initval "nullptr" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer)
|
:save-fun #'save-pointer :load-fun #'load-pointer)
|
||||||
@ -426,10 +426,12 @@ a preceeding `MATCH`), or multiple nodes (`MATCH ... CREATE` or
|
|||||||
(lcp:define-class create-expand (logical-operator)
|
(lcp:define-class create-expand (logical-operator)
|
||||||
(
|
(
|
||||||
;; info on what's getting expanded
|
;; info on what's getting expanded
|
||||||
(node-atom "NodeAtom *" :save-fun #'save-pointer :load-fun #'load-pointer
|
(node-atom "NodeAtom *" :reader t
|
||||||
|
:save-fun #'save-pointer :load-fun #'load-pointer
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *"))
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *"))
|
||||||
(edge-atom "EdgeAtom *" :save-fun #'save-pointer :load-fun #'load-pointer
|
(edge-atom "EdgeAtom *" :reader t
|
||||||
|
:save-fun #'save-pointer :load-fun #'load-pointer
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "EdgeAtom *"))
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "EdgeAtom *"))
|
||||||
;; the input op and the symbol under which the op's result
|
;; the input op and the symbol under which the op's result
|
||||||
@ -437,7 +439,7 @@ a preceeding `MATCH`), or multiple nodes (`MATCH ... CREATE` or
|
|||||||
(input "std::shared_ptr<LogicalOperator>"
|
(input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(input-symbol "Symbol")
|
(input-symbol "Symbol" :reader t)
|
||||||
(existing-node :bool :documentation
|
(existing-node :bool :documentation
|
||||||
"if the given node atom refers to an existing node (either matched or created)"))
|
"if the given node atom refers to an existing node (either matched or created)"))
|
||||||
(:documentation
|
(:documentation
|
||||||
@ -1157,7 +1159,7 @@ RETURN clause) the Produce's pull succeeds exactly once.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(expressions "std::vector<Expression *>"
|
(expressions "std::vector<Expression *>" :reader t
|
||||||
:capnp-type "List(Ast.Tree)"
|
:capnp-type "List(Ast.Tree)"
|
||||||
:capnp-save (save-ast-vector "Expression *")
|
:capnp-save (save-ast-vector "Expression *")
|
||||||
:capnp-load (load-ast-vector "Expression *")
|
:capnp-load (load-ast-vector "Expression *")
|
||||||
@ -1206,11 +1208,11 @@ Has a flag for using DETACH DELETE when deleting vertices.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(lhs "PropertyLookup *"
|
(lhs "PropertyLookup *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer)
|
:save-fun #'save-pointer :load-fun #'load-pointer)
|
||||||
(rhs "Expression *"
|
(rhs "Expression *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer))
|
:save-fun #'save-pointer :load-fun #'load-pointer))
|
||||||
@ -1256,8 +1258,8 @@ can be stored (a TypedValue that can be converted to PropertyValue).")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(input-symbol "Symbol")
|
(input-symbol "Symbol" :reader t)
|
||||||
(rhs "Expression *"
|
(rhs "Expression *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer)
|
:save-fun #'save-pointer :load-fun #'load-pointer)
|
||||||
@ -1327,7 +1329,7 @@ that the old props are discarded and replaced with new ones.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(input-symbol "Symbol")
|
(input-symbol "Symbol" :reader t)
|
||||||
(labels "std::vector<storage::Label>"
|
(labels "std::vector<storage::Label>"
|
||||||
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
||||||
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
||||||
@ -1371,7 +1373,7 @@ It does NOT remove labels that are already set on that Vertex.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(lhs "PropertyLookup *"
|
(lhs "PropertyLookup *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer))
|
:save-fun #'save-pointer :load-fun #'load-pointer))
|
||||||
@ -1415,7 +1417,7 @@ It does NOT remove labels that are already set on that Vertex.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(input-symbol "Symbol")
|
(input-symbol "Symbol" :reader t)
|
||||||
(labels "std::vector<storage::Label>"
|
(labels "std::vector<storage::Label>"
|
||||||
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
||||||
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
||||||
@ -1763,7 +1765,7 @@ elements are in an undefined state after aggregation.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(expression "Expression *"
|
(expression "Expression *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer))
|
:save-fun #'save-pointer :load-fun #'load-pointer))
|
||||||
@ -1820,7 +1822,7 @@ operator's implementation does not expect this.")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(expression "Expression *"
|
(expression "Expression *" :reader t
|
||||||
:capnp-type "Ast.Tree" :capnp-init nil
|
:capnp-type "Ast.Tree" :capnp-init nil
|
||||||
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
||||||
:save-fun #'save-pointer :load-fun #'load-pointer))
|
:save-fun #'save-pointer :load-fun #'load-pointer))
|
||||||
@ -2124,7 +2126,7 @@ Input is optional (unwind can be the first clause in a query).")
|
|||||||
((input "std::shared_ptr<LogicalOperator>"
|
((input "std::shared_ptr<LogicalOperator>"
|
||||||
:capnp-save #'save-operator-pointer
|
:capnp-save #'save-operator-pointer
|
||||||
:capnp-load #'load-operator-pointer)
|
:capnp-load #'load-operator-pointer)
|
||||||
(value-symbols "std::vector<Symbol>"
|
(value-symbols "std::vector<Symbol>" :reader t
|
||||||
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
||||||
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
||||||
(:documentation
|
(:documentation
|
||||||
|
@ -550,7 +550,17 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PreVisit(query::plan::Cartesian &op) override {
|
bool PreVisit(query::plan::Cartesian &op) override {
|
||||||
WithPrintLn([](auto &out) { out << "* Cartesian"; });
|
WithPrintLn([&op](auto &out) {
|
||||||
|
out << "* Cartesian {";
|
||||||
|
utils::PrintIterable(
|
||||||
|
out, op.left_symbols(), ", ",
|
||||||
|
[](auto &out, const auto &sym) { out << sym.name(); });
|
||||||
|
out << " : ";
|
||||||
|
utils::PrintIterable(
|
||||||
|
out, op.right_symbols(), ", ",
|
||||||
|
[](auto &out, const auto &sym) { out << sym.name(); });
|
||||||
|
out << "}";
|
||||||
|
});
|
||||||
Branch(*op.right_op());
|
Branch(*op.right_op());
|
||||||
op.left_op()->Accept(*this);
|
op.left_op()->Accept(*this);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
///
|
|
||||||
/// @file
|
/// @file
|
||||||
/// This file provides macros for easier construction of openCypher query AST.
|
/// This file provides macros for easier construction of openCypher query AST.
|
||||||
/// The usage of macros is very similar to how one would write openCypher. For
|
/// The usage of macros is very similar to how one would write openCypher. For
|
||||||
/// example:
|
/// example:
|
||||||
///
|
///
|
||||||
/// AstStorage storage; // Macros rely on storage being in scope.
|
/// AstStorage storage; // Macros rely on storage being in scope.
|
||||||
/// // PROPERTY_LOOKUP and PROPERTY_PAIR macros rely on database::SingleNode
|
/// // PROPERTY_LOOKUP and PROPERTY_PAIR macros
|
||||||
|
/// // rely on a DbAccessor named dba.
|
||||||
/// database::SingleNode db;
|
/// database::SingleNode db;
|
||||||
|
/// database::GraphDbAccessor dba(db);
|
||||||
///
|
///
|
||||||
/// QUERY(MATCH(PATTERN(NODE("n"), EDGE("e"), NODE("m"))),
|
/// QUERY(MATCH(PATTERN(NODE("n"), EDGE("e"), NODE("m"))),
|
||||||
/// WHERE(LESS(PROPERTY_LOOKUP("e", edge_prop), LITERAL(3))),
|
/// WHERE(LESS(PROPERTY_LOOKUP("e", edge_prop), LITERAL(3))),
|
||||||
@ -18,7 +19,6 @@
|
|||||||
/// resolution and template magic to provide a type safe way of constructing
|
/// resolution and template magic to provide a type safe way of constructing
|
||||||
/// queries. Although the functions can be used by themselves, it is more
|
/// queries. Although the functions can be used by themselves, it is more
|
||||||
/// convenient to use the macros.
|
/// convenient to use the macros.
|
||||||
///
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -28,10 +28,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "database/graph_db.hpp"
|
|
||||||
#include "database/graph_db_accessor.hpp"
|
|
||||||
#include "query/frontend/ast/ast.hpp"
|
#include "query/frontend/ast/ast.hpp"
|
||||||
#include "query/interpret/awesome_memgraph_functions.hpp"
|
|
||||||
#include "storage/types.hpp"
|
#include "storage/types.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
@ -91,12 +88,10 @@ auto FillOrderBy(OrderBy &order_by, Expression *expression, T... rest) {
|
|||||||
FillOrderBy(order_by, rest...);
|
FillOrderBy(order_by, rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create OrderBy expressions.
|
/// Create OrderBy expressions.
|
||||||
///
|
///
|
||||||
/// The supported combination of arguments is: (Expression, [Ordering])+
|
/// The supported combination of arguments is: (Expression, [Ordering])+
|
||||||
/// Since the Ordering is optional, by default it is ascending.
|
/// Since the Ordering is optional, by default it is ascending.
|
||||||
///
|
|
||||||
template <class... T>
|
template <class... T>
|
||||||
auto GetOrderBy(T... exprs) {
|
auto GetOrderBy(T... exprs) {
|
||||||
OrderBy order_by;
|
OrderBy order_by;
|
||||||
@ -104,41 +99,42 @@ auto GetOrderBy(T... exprs) {
|
|||||||
return order_by;
|
return order_by;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create PropertyLookup with given name and property.
|
/// Create PropertyLookup with given name and property.
|
||||||
///
|
///
|
||||||
/// Name is used to create the Identifier which is used for property lookup.
|
/// Name is used to create the Identifier which is used for property lookup.
|
||||||
///
|
template <class TDbAccessor>
|
||||||
auto GetPropertyLookup(AstStorage &storage, database::GraphDb &db,
|
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &dba,
|
||||||
const std::string &name, storage::Property property) {
|
const std::string &name, storage::Property property) {
|
||||||
database::GraphDbAccessor dba(db);
|
|
||||||
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
||||||
dba.PropertyName(property), property);
|
dba.PropertyName(property), property);
|
||||||
}
|
}
|
||||||
auto GetPropertyLookup(AstStorage &storage, database::GraphDb &db,
|
|
||||||
Expression *expr, storage::Property property) {
|
template <class TDbAccessor>
|
||||||
database::GraphDbAccessor dba(db);
|
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &dba, Expression *expr,
|
||||||
|
storage::Property property) {
|
||||||
return storage.Create<PropertyLookup>(expr, dba.PropertyName(property),
|
return storage.Create<PropertyLookup>(expr, dba.PropertyName(property),
|
||||||
property);
|
property);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class TDbAccessor>
|
||||||
auto GetPropertyLookup(
|
auto GetPropertyLookup(
|
||||||
AstStorage &storage, database::GraphDb &, const std::string &name,
|
AstStorage &storage, TDbAccessor &, const std::string &name,
|
||||||
const std::pair<std::string, storage::Property> &prop_pair) {
|
const std::pair<std::string, storage::Property> &prop_pair) {
|
||||||
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
|
||||||
prop_pair.first, prop_pair.second);
|
prop_pair.first, prop_pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class TDbAccessor>
|
||||||
auto GetPropertyLookup(
|
auto GetPropertyLookup(
|
||||||
AstStorage &storage, database::GraphDb &, Expression *expr,
|
AstStorage &storage, TDbAccessor &, Expression *expr,
|
||||||
const std::pair<std::string, storage::Property> &prop_pair) {
|
const std::pair<std::string, storage::Property> &prop_pair) {
|
||||||
return storage.Create<PropertyLookup>(expr, prop_pair.first,
|
return storage.Create<PropertyLookup>(expr, prop_pair.first,
|
||||||
prop_pair.second);
|
prop_pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create an EdgeAtom with given name, direction and edge_type.
|
/// Create an EdgeAtom with given name, direction and edge_type.
|
||||||
///
|
///
|
||||||
/// Name is used to create the Identifier which is assigned to the edge.
|
/// Name is used to create the Identifier which is assigned to the edge.
|
||||||
///
|
|
||||||
auto GetEdge(AstStorage &storage, const std::string &name,
|
auto GetEdge(AstStorage &storage, const std::string &name,
|
||||||
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
|
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
|
||||||
const std::vector<storage::EdgeType> &edge_types = {}) {
|
const std::vector<storage::EdgeType> &edge_types = {}) {
|
||||||
@ -146,12 +142,10 @@ auto GetEdge(AstStorage &storage, const std::string &name,
|
|||||||
EdgeAtom::Type::SINGLE, dir, edge_types);
|
EdgeAtom::Type::SINGLE, dir, edge_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a variable length expansion EdgeAtom with given name, direction and
|
/// Create a variable length expansion EdgeAtom with given name, direction and
|
||||||
/// edge_type.
|
/// edge_type.
|
||||||
///
|
///
|
||||||
/// Name is used to create the Identifier which is assigned to the edge.
|
/// Name is used to create the Identifier which is assigned to the edge.
|
||||||
///
|
|
||||||
auto GetEdgeVariable(AstStorage &storage, const std::string &name,
|
auto GetEdgeVariable(AstStorage &storage, const std::string &name,
|
||||||
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
|
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
|
||||||
const std::vector<storage::EdgeType> &edge_types = {},
|
const std::vector<storage::EdgeType> &edge_types = {},
|
||||||
@ -169,11 +163,9 @@ auto GetEdgeVariable(AstStorage &storage, const std::string &name,
|
|||||||
return r_val;
|
return r_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a NodeAtom with given name and label.
|
/// Create a NodeAtom with given name and label.
|
||||||
///
|
///
|
||||||
/// Name is used to create the Identifier which is assigned to the node.
|
/// Name is used to create the Identifier which is assigned to the node.
|
||||||
///
|
|
||||||
auto GetNode(AstStorage &storage, const std::string &name,
|
auto GetNode(AstStorage &storage, const std::string &name,
|
||||||
std::experimental::optional<storage::Label> label =
|
std::experimental::optional<storage::Label> label =
|
||||||
std::experimental::nullopt) {
|
std::experimental::nullopt) {
|
||||||
@ -182,9 +174,7 @@ auto GetNode(AstStorage &storage, const std::string &name,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a Pattern with given atoms.
|
/// Create a Pattern with given atoms.
|
||||||
///
|
|
||||||
auto GetPattern(AstStorage &storage, std::vector<PatternAtom *> atoms) {
|
auto GetPattern(AstStorage &storage, std::vector<PatternAtom *> atoms) {
|
||||||
auto pattern = storage.Create<Pattern>();
|
auto pattern = storage.Create<Pattern>();
|
||||||
pattern->identifier_ =
|
pattern->identifier_ =
|
||||||
@ -193,9 +183,7 @@ auto GetPattern(AstStorage &storage, std::vector<PatternAtom *> atoms) {
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a Pattern with given name and atoms.
|
/// Create a Pattern with given name and atoms.
|
||||||
///
|
|
||||||
auto GetPattern(AstStorage &storage, const std::string &name,
|
auto GetPattern(AstStorage &storage, const std::string &name,
|
||||||
std::vector<PatternAtom *> atoms) {
|
std::vector<PatternAtom *> atoms) {
|
||||||
auto pattern = storage.Create<Pattern>();
|
auto pattern = storage.Create<Pattern>();
|
||||||
@ -204,11 +192,9 @@ auto GetPattern(AstStorage &storage, const std::string &name,
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// This function fills an AST node which with given patterns.
|
/// This function fills an AST node which with given patterns.
|
||||||
///
|
///
|
||||||
/// The function is most commonly used to create Match and Create clauses.
|
/// The function is most commonly used to create Match and Create clauses.
|
||||||
///
|
|
||||||
template <class TWithPatterns>
|
template <class TWithPatterns>
|
||||||
auto GetWithPatterns(TWithPatterns *with_patterns,
|
auto GetWithPatterns(TWithPatterns *with_patterns,
|
||||||
std::vector<Pattern *> patterns) {
|
std::vector<Pattern *> patterns) {
|
||||||
@ -217,9 +203,7 @@ auto GetWithPatterns(TWithPatterns *with_patterns,
|
|||||||
return with_patterns;
|
return with_patterns;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a query with given clauses.
|
/// Create a query with given clauses.
|
||||||
///
|
|
||||||
|
|
||||||
auto GetSingleQuery(SingleQuery *single_query, Clause *clause) {
|
auto GetSingleQuery(SingleQuery *single_query, Clause *clause) {
|
||||||
single_query->clauses_.emplace_back(clause);
|
single_query->clauses_.emplace_back(clause);
|
||||||
@ -351,7 +335,6 @@ void FillReturnBody(AstStorage &storage, ReturnBody &body,
|
|||||||
FillReturnBody(storage, body, rest...);
|
FillReturnBody(storage, body, rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create the return clause with given expressions.
|
/// Create the return clause with given expressions.
|
||||||
///
|
///
|
||||||
/// The supported expression combination of arguments is:
|
/// The supported expression combination of arguments is:
|
||||||
@ -373,7 +356,6 @@ auto GetReturn(AstStorage &storage, bool distinct, T... exprs) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create the with clause with given expressions.
|
/// Create the with clause with given expressions.
|
||||||
///
|
///
|
||||||
/// The supported expression combination is the same as for @c GetReturn.
|
/// The supported expression combination is the same as for @c GetReturn.
|
||||||
@ -387,9 +369,7 @@ auto GetWith(AstStorage &storage, bool distinct, T... exprs) {
|
|||||||
return with;
|
return with;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create the UNWIND clause with given named expression.
|
/// Create the UNWIND clause with given named expression.
|
||||||
///
|
|
||||||
auto GetUnwind(AstStorage &storage, NamedExpression *named_expr) {
|
auto GetUnwind(AstStorage &storage, NamedExpression *named_expr) {
|
||||||
return storage.Create<query::Unwind>(named_expr);
|
return storage.Create<query::Unwind>(named_expr);
|
||||||
}
|
}
|
||||||
@ -398,9 +378,7 @@ auto GetUnwind(AstStorage &storage, Expression *expr, NamedExpression *as) {
|
|||||||
return GetUnwind(storage, as);
|
return GetUnwind(storage, as);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create the delete clause with given named expressions.
|
/// Create the delete clause with given named expressions.
|
||||||
///
|
|
||||||
auto GetDelete(AstStorage &storage, std::vector<Expression *> exprs,
|
auto GetDelete(AstStorage &storage, std::vector<Expression *> exprs,
|
||||||
bool detach = false) {
|
bool detach = false) {
|
||||||
auto del = storage.Create<Delete>();
|
auto del = storage.Create<Delete>();
|
||||||
@ -410,52 +388,40 @@ auto GetDelete(AstStorage &storage, std::vector<Expression *> exprs,
|
|||||||
return del;
|
return del;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a set property clause for given property lookup and the right hand
|
/// Create a set property clause for given property lookup and the right hand
|
||||||
/// side expression.
|
/// side expression.
|
||||||
///
|
|
||||||
auto GetSet(AstStorage &storage, PropertyLookup *prop_lookup,
|
auto GetSet(AstStorage &storage, PropertyLookup *prop_lookup,
|
||||||
Expression *expr) {
|
Expression *expr) {
|
||||||
return storage.Create<SetProperty>(prop_lookup, expr);
|
return storage.Create<SetProperty>(prop_lookup, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a set properties clause for given identifier name and the right hand
|
/// Create a set properties clause for given identifier name and the right hand
|
||||||
/// side expression.
|
/// side expression.
|
||||||
///
|
|
||||||
auto GetSet(AstStorage &storage, const std::string &name, Expression *expr,
|
auto GetSet(AstStorage &storage, const std::string &name, Expression *expr,
|
||||||
bool update = false) {
|
bool update = false) {
|
||||||
return storage.Create<SetProperties>(storage.Create<Identifier>(name), expr,
|
return storage.Create<SetProperties>(storage.Create<Identifier>(name), expr,
|
||||||
update);
|
update);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a set labels clause for given identifier name and labels.
|
/// Create a set labels clause for given identifier name and labels.
|
||||||
///
|
|
||||||
auto GetSet(AstStorage &storage, const std::string &name,
|
auto GetSet(AstStorage &storage, const std::string &name,
|
||||||
std::vector<storage::Label> labels) {
|
std::vector<storage::Label> labels) {
|
||||||
return storage.Create<SetLabels>(storage.Create<Identifier>(name), labels);
|
return storage.Create<SetLabels>(storage.Create<Identifier>(name), labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a remove property clause for given property lookup
|
/// Create a remove property clause for given property lookup
|
||||||
///
|
|
||||||
auto GetRemove(AstStorage &storage, PropertyLookup *prop_lookup) {
|
auto GetRemove(AstStorage &storage, PropertyLookup *prop_lookup) {
|
||||||
return storage.Create<RemoveProperty>(prop_lookup);
|
return storage.Create<RemoveProperty>(prop_lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a remove labels clause for given identifier name and labels.
|
/// Create a remove labels clause for given identifier name and labels.
|
||||||
///
|
|
||||||
auto GetRemove(AstStorage &storage, const std::string &name,
|
auto GetRemove(AstStorage &storage, const std::string &name,
|
||||||
std::vector<storage::Label> labels) {
|
std::vector<storage::Label> labels) {
|
||||||
return storage.Create<RemoveLabels>(storage.Create<Identifier>(name), labels);
|
return storage.Create<RemoveLabels>(storage.Create<Identifier>(name), labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a Merge clause for given Pattern with optional OnMatch and OnCreate
|
/// Create a Merge clause for given Pattern with optional OnMatch and OnCreate
|
||||||
/// parts.
|
/// parts.
|
||||||
///
|
|
||||||
auto GetMerge(AstStorage &storage, Pattern *pattern,
|
auto GetMerge(AstStorage &storage, Pattern *pattern,
|
||||||
OnCreate on_create = OnCreate{}) {
|
OnCreate on_create = OnCreate{}) {
|
||||||
auto *merge = storage.Create<query::Merge>();
|
auto *merge = storage.Create<query::Merge>();
|
||||||
@ -476,7 +442,6 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
|
|||||||
|
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
|
||||||
///
|
|
||||||
/// All the following macros implicitly pass `storage` variable to functions.
|
/// All the following macros implicitly pass `storage` variable to functions.
|
||||||
/// You need to have `AstStorage storage;` somewhere in scope to use them.
|
/// You need to have `AstStorage storage;` somewhere in scope to use them.
|
||||||
/// Refer to function documentation to see what the macro does.
|
/// Refer to function documentation to see what the macro does.
|
||||||
@ -487,7 +452,6 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
|
|||||||
/// AstStorage storage;
|
/// AstStorage storage;
|
||||||
/// auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
|
/// auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
|
||||||
/// RETURN(NEXPR("new_name"), IDENT("m")));
|
/// RETURN(NEXPR("new_name"), IDENT("m")));
|
||||||
///
|
|
||||||
#define NODE(...) query::test_common::GetNode(storage, __VA_ARGS__)
|
#define NODE(...) query::test_common::GetNode(storage, __VA_ARGS__)
|
||||||
#define EDGE(...) query::test_common::GetEdge(storage, __VA_ARGS__)
|
#define EDGE(...) query::test_common::GetEdge(storage, __VA_ARGS__)
|
||||||
#define EDGE_VARIABLE(...) \
|
#define EDGE_VARIABLE(...) \
|
||||||
@ -515,10 +479,9 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
|
|||||||
std::unordered_map<std::pair<std::string, storage::Property>, \
|
std::unordered_map<std::pair<std::string, storage::Property>, \
|
||||||
query::Expression *>{__VA_ARGS__})
|
query::Expression *>{__VA_ARGS__})
|
||||||
#define PROPERTY_PAIR(property_name) \
|
#define PROPERTY_PAIR(property_name) \
|
||||||
std::make_pair(property_name, \
|
std::make_pair(property_name, dba.Property(property_name))
|
||||||
database::GraphDbAccessor(db).Property(property_name))
|
|
||||||
#define PROPERTY_LOOKUP(...) \
|
#define PROPERTY_LOOKUP(...) \
|
||||||
query::test_common::GetPropertyLookup(storage, db, __VA_ARGS__)
|
query::test_common::GetPropertyLookup(storage, dba, __VA_ARGS__)
|
||||||
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
|
#define NEXPR(name, expr) storage.Create<query::NamedExpression>((name), (expr))
|
||||||
// AS is alternative to NEXPR which does not initialize NamedExpression with
|
// AS is alternative to NEXPR which does not initialize NamedExpression with
|
||||||
// Expression. It should be used with RETURN or WITH. For example:
|
// Expression. It should be used with RETURN or WITH. For example:
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user