Reviewers: mculinovic, teon.banek, ipaljak, buda, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1567
2557 lines
101 KiB
Plaintext
2557 lines
101 KiB
Plaintext
#>cpp
|
|
/** @file */
|
|
|
|
#pragma once
|
|
|
|
#include <experimental/optional>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "query/common.hpp"
|
|
#include "query/frontend/ast/ast.hpp"
|
|
#include "query/frontend/semantic/symbol.hpp"
|
|
#include "query/typed_value.hpp"
|
|
#include "storage/types.hpp"
|
|
#include "utils/bound.hpp"
|
|
#include "utils/future.hpp"
|
|
#include "utils/hashing/fnv.hpp"
|
|
#include "utils/visitor.hpp"
|
|
|
|
// TODO: It makes no sense for this file to include distributed_ops Cap'n
|
|
// Proto schema. Unfortunately, forward declares of nested capnp types do not
|
|
// work, so we cannot use that mechanism. Perhaps we want to generate
|
|
// serialization code based only on type info, similarly to RPCs. This
|
|
// requires rework of LCP and we will need to have a sort of 'register'
|
|
// mechanism to fill the table of (type info -> operator constructor).
|
|
#include "query/plan/distributed_ops.capnp.h"
|
|
|
|
namespace database {
|
|
class GraphDbAccessor;
|
|
}
|
|
cpp<#
|
|
|
|
(lcp:namespace query)
|
|
|
|
#>cpp
|
|
class Context;
|
|
class ExpressionEvaluator;
|
|
class Frame;
|
|
class SymbolTable;
|
|
cpp<#
|
|
|
|
(lcp:namespace plan)
|
|
|
|
#>cpp
|
|
/** @brief Base class for iteration cursors of @c LogicalOperator classes.
|
|
*
|
|
* Each @c LogicalOperator must produce a concrete @c Cursor, which provides
|
|
* the iteration mechanism.
|
|
*/
|
|
class Cursor {
|
|
public:
|
|
/** @brief Run an iteration of a @c LogicalOperator.
|
|
*
|
|
* Since operators may be chained, the iteration may pull results from
|
|
* multiple operators.
|
|
*
|
|
* @param Frame May be read from or written to while performing the
|
|
* iteration.
|
|
* @param Context Used to get the position of symbols in frame and other
|
|
* information.
|
|
*/
|
|
virtual bool Pull(Frame &, Context &) = 0;
|
|
|
|
/**
|
|
* Resets the Cursor to it's initial state.
|
|
*/
|
|
virtual void Reset() = 0;
|
|
|
|
virtual ~Cursor() {}
|
|
};
|
|
|
|
class Once;
|
|
class CreateNode;
|
|
class CreateExpand;
|
|
class ScanAll;
|
|
class ScanAllByLabel;
|
|
class ScanAllByLabelPropertyRange;
|
|
class ScanAllByLabelPropertyValue;
|
|
class Expand;
|
|
class ExpandVariable;
|
|
class ConstructNamedPath;
|
|
class Filter;
|
|
class Produce;
|
|
class Delete;
|
|
class SetProperty;
|
|
class SetProperties;
|
|
class SetLabels;
|
|
class RemoveProperty;
|
|
class RemoveLabels;
|
|
template <typename TAccessor>
|
|
class ExpandUniquenessFilter;
|
|
class Accumulate;
|
|
class Aggregate;
|
|
class Skip;
|
|
class Limit;
|
|
class OrderBy;
|
|
class Merge;
|
|
class Optional;
|
|
class Unwind;
|
|
class Distinct;
|
|
class CreateIndex;
|
|
class Union;
|
|
class Cartesian;
|
|
class AuthHandler;
|
|
class CreateStream;
|
|
class DropStream;
|
|
class ShowStreams;
|
|
class StartStopStream;
|
|
class StartStopAllStreams;
|
|
class TestStream;
|
|
class Explain;
|
|
|
|
using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor<
|
|
Once, CreateNode, CreateExpand, ScanAll, ScanAllByLabel,
|
|
ScanAllByLabelPropertyRange, ScanAllByLabelPropertyValue, Expand,
|
|
ExpandVariable, ConstructNamedPath, Filter, Produce, Delete, SetProperty,
|
|
SetProperties, SetLabels, RemoveProperty, RemoveLabels,
|
|
ExpandUniquenessFilter<VertexAccessor>,
|
|
ExpandUniquenessFilter<EdgeAccessor>, Accumulate, Aggregate, Skip, Limit,
|
|
OrderBy, Merge, Optional, Unwind, Distinct, Union, Cartesian, Explain>;
|
|
|
|
using LogicalOperatorLeafVisitor =
|
|
::utils::LeafVisitor<Once, CreateIndex, AuthHandler, CreateStream,
|
|
DropStream, ShowStreams, StartStopStream,
|
|
StartStopAllStreams, TestStream>;
|
|
|
|
/**
|
|
* @brief Base class for hierarhical visitors of @c LogicalOperator class
|
|
* hierarchy.
|
|
*/
|
|
class HierarchicalLogicalOperatorVisitor
|
|
: public LogicalOperatorCompositeVisitor,
|
|
public LogicalOperatorLeafVisitor {
|
|
public:
|
|
using LogicalOperatorCompositeVisitor::PostVisit;
|
|
using LogicalOperatorCompositeVisitor::PreVisit;
|
|
using LogicalOperatorLeafVisitor::Visit;
|
|
using typename LogicalOperatorLeafVisitor::ReturnType;
|
|
};
|
|
cpp<#
|
|
|
|
(lcp:capnp-namespace "query::plan")
|
|
|
|
(lcp:capnp-import 'utils "/utils/serialization.capnp")
|
|
(lcp:capnp-import 'storage "/storage/serialization.capnp")
|
|
(lcp:capnp-import 'ast "/query/frontend/ast/ast.capnp")
|
|
(lcp:capnp-import 'semantic "/query/frontend/semantic/symbol.capnp")
|
|
(lcp:capnp-import 'common "/query/common.capnp")
|
|
|
|
(lcp:capnp-type-conversion "Symbol" "Semantic.Symbol")
|
|
(lcp:capnp-type-conversion "storage::Label" "Storage.Common")
|
|
(lcp:capnp-type-conversion "storage::Property" "Storage.Common")
|
|
(lcp:capnp-type-conversion "storage::EdgeType" "Storage.Common")
|
|
(lcp:capnp-type-conversion "GraphView" "Common.GraphView")
|
|
|
|
(lcp:define-class logical-operator ("::utils::Visitable<HierarchicalLogicalOperatorVisitor>")
|
|
()
|
|
(:documentation
|
|
"Base class for logical operators.
|
|
|
|
Each operator describes an operation, which is to be performed on the
|
|
database. Operators are iterated over using a @c Cursor. Various operators
|
|
can serve as inputs to others and thus a sequence of operations is formed.")
|
|
(:public
|
|
#>cpp
|
|
virtual ~LogicalOperator() {}
|
|
|
|
/** Constructs a @c Cursor which is used to run this operator.
|
|
*
|
|
* @param database::GraphDbAccessor Used to perform operations on the
|
|
* database.
|
|
*/
|
|
virtual std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &) const = 0;
|
|
|
|
/** Return @c Symbol vector where the query results will be stored.
|
|
*
|
|
* Currently, outputs symbols are generated in @c Produce and @c Union
|
|
* operators. @c Skip, @c Limit, @c OrderBy and @c Distinct propagate the
|
|
* symbols from @c Produce (if it exists as input operator).
|
|
*
|
|
* @param SymbolTable used to find symbols for expressions.
|
|
* @return std::vector<Symbol> used for results.
|
|
*/
|
|
virtual std::vector<Symbol> OutputSymbols(const SymbolTable &) const {
|
|
return std::vector<Symbol>();
|
|
}
|
|
|
|
/**
|
|
* Symbol vector whose values are modified by this operator sub-tree.
|
|
*
|
|
* This is different than @c OutputSymbols, because it returns all of the
|
|
* modified symbols, including those that may not be returned as the
|
|
* result of the query. Note that the modified symbols will not contain
|
|
* those that should not be read after the operator is processed.
|
|
*
|
|
* For example, `MATCH (n)-[e]-(m) RETURN n AS l` will generate `ScanAll (n) >
|
|
* Expand (e, m) > Produce (l)`. The modified symbols on Produce sub-tree will
|
|
* be `l`, the same as output symbols, because it isn't valid to read `n`, `e`
|
|
* nor `m` after Produce. On the other hand, modified symbols from Expand
|
|
* contain `e` and `m`, as well as `n`, while output symbols are empty.
|
|
* Modified symbols from ScanAll contain only `n`, while output symbols are
|
|
* also empty.
|
|
*/
|
|
virtual std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const = 0;
|
|
|
|
/**
|
|
* Returns true if the operator takes only one input operator.
|
|
* NOTE: When this method returns true, you may use `input` and `set_input`
|
|
* methods.
|
|
*/
|
|
virtual bool HasSingleInput() const = 0;
|
|
|
|
/**
|
|
* Returns the input operator if it has any.
|
|
* NOTE: This should only be called if `HasSingleInput() == true`.
|
|
*/
|
|
virtual std::shared_ptr<LogicalOperator> input() const = 0;
|
|
/**
|
|
* Set a different input on this operator.
|
|
* NOTE: This should only be called if `HasSingleInput() == true`.
|
|
*/
|
|
virtual void set_input(std::shared_ptr<LogicalOperator>) = 0;
|
|
|
|
struct SaveHelper {
|
|
std::vector<int> saved_ast_uids;
|
|
std::vector<LogicalOperator *> saved_ops;
|
|
};
|
|
|
|
struct LoadHelper {
|
|
AstStorage ast_storage;
|
|
std::vector<int> loaded_ast_uids;
|
|
std::vector<std::pair<uint64_t, std::shared_ptr<LogicalOperator>>>
|
|
loaded_ops;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp :base t
|
|
:save-args '((helper "SaveHelper *"))
|
|
:load-args '((helper "LoadHelper *"))))
|
|
|
|
(defun save-ast-pointer (builder member)
|
|
(let ((member-getter (remove #\_ (string-capitalize member))))
|
|
#>cpp
|
|
if (${member}) {
|
|
auto ${member}_builder = ${builder}->init${member-getter}();
|
|
${member}->Save(&${member}_builder, &helper->saved_ast_uids);
|
|
}
|
|
cpp<#))
|
|
|
|
(defun load-ast-pointer (ast-type)
|
|
(lambda (reader member)
|
|
(let ((member-getter (remove #\_ (string-capitalize member))))
|
|
#>cpp
|
|
if (${reader}.has${member-getter}())
|
|
${member} = static_cast<${ast-type}>(helper->ast_storage.Load(${reader}.get${member-getter}(), &helper->loaded_ast_uids));
|
|
else
|
|
${member} = nullptr;
|
|
cpp<#)))
|
|
|
|
(defun save-ast-vector (ast-type)
|
|
(lcp:capnp-save-vector "::query::capnp::Tree" ast-type
|
|
"[helper](auto *builder, const auto &val) {
|
|
val->Save(builder, &helper->saved_ast_uids);
|
|
}"))
|
|
|
|
(defun load-ast-vector (ast-type)
|
|
(lcp:capnp-load-vector "::query::capnp::Tree" ast-type
|
|
(format
|
|
nil
|
|
"[helper](const auto &reader) {
|
|
// We expect the unsafe downcast via static_cast to work.
|
|
return static_cast<~A>(helper->ast_storage.Load(reader, &helper->loaded_ast_uids));
|
|
}" ast-type)))
|
|
|
|
(defun save-operator-pointer (builder member-name)
|
|
#>cpp
|
|
utils::SaveSharedPtr<capnp::LogicalOperator, LogicalOperator>(${member-name}, &${builder},
|
|
[helper](auto *builder, const auto &val) {
|
|
val.Save(builder, helper);
|
|
}, &helper->saved_ops);
|
|
cpp<#)
|
|
|
|
(defun load-operator-pointer (reader member-name)
|
|
#>cpp
|
|
${member-name} = utils::LoadSharedPtr<capnp::LogicalOperator, LogicalOperator>(${reader},
|
|
[helper](const auto &reader) {
|
|
auto op = LogicalOperator::Construct(reader);
|
|
op->Load(reader, helper);
|
|
return op.release();
|
|
}, &helper->loaded_ops);
|
|
cpp<#)
|
|
|
|
(lcp:define-class once (logical-operator)
|
|
()
|
|
(:documentation
|
|
"A logical operator whose Cursor returns true on the first Pull
|
|
and false on every following Pull.")
|
|
(:public
|
|
#>cpp
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
class OnceCursor : public Cursor {
|
|
public:
|
|
OnceCursor() {}
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
bool did_pull_{false};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class create-node (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(node-atom "NodeAtom *" :initval "nullptr" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *"))
|
|
(on-random-worker :bool :initval "false"))
|
|
(:documentation
|
|
"Operator for creating a node.
|
|
|
|
This op is used both for creating a single node (`CREATE` statement without
|
|
a preceeding `MATCH`), or multiple nodes (`MATCH ... CREATE` or
|
|
`CREATE (), () ...`).
|
|
|
|
@sa CreateExpand")
|
|
(:public
|
|
#>cpp
|
|
/**
|
|
* @param input Optional. If @c nullptr, then a single node will be
|
|
* created (a single successful @c Cursor::Pull from this op's @c Cursor).
|
|
* If a valid input, then a node will be created for each
|
|
* successful pull from the given input.
|
|
* @param node_atom @c NodeAtom with information on how to create a node.
|
|
* @param on_random_worker If the node should be created locally or on random
|
|
* worker.
|
|
*/
|
|
CreateNode(const std::shared_ptr<LogicalOperator> &input, NodeAtom *node_atom,
|
|
bool on_random_worker);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
|
|
auto on_random_worker() const { return on_random_worker_; }
|
|
void set_on_random_worker(bool v) { on_random_worker_ = v; }
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
CreateNode() {}
|
|
|
|
class CreateNodeCursor : public Cursor {
|
|
public:
|
|
CreateNodeCursor(const CreateNode &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const CreateNode &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class create-expand (logical-operator)
|
|
(
|
|
;; info on what's getting expanded
|
|
(node-atom "NodeAtom *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "NodeAtom *"))
|
|
(edge-atom "EdgeAtom *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "EdgeAtom *"))
|
|
;; the input op and the symbol under which the op's result
|
|
;; can be found in the frame
|
|
(input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-symbol "Symbol" :reader t)
|
|
(existing-node :bool :documentation
|
|
"if the given node atom refers to an existing node (either matched or created)"))
|
|
(:documentation
|
|
"Operator for creating edges and destination nodes.
|
|
|
|
This operator extends already created nodes with an edge. If the other node
|
|
on the edge does not exist, it will be created. For example, in `MATCH (n)
|
|
CREATE (n) -[r:r]-> (n)` query, this operator will create just the edge `r`.
|
|
In `MATCH (n) CREATE (n) -[r:r]-> (m)` query, the operator will create both
|
|
the edge `r` and the node `m`. In case of `CREATE (n) -[r:r]-> (m)` the
|
|
first node `n` is created by @c CreateNode operator, while @c CreateExpand
|
|
will create the edge `r` and `m`. Similarly, multiple @c CreateExpand are
|
|
chained in cases when longer paths need creating.
|
|
|
|
@sa CreateNode")
|
|
(:public
|
|
#>cpp
|
|
/** @brief Construct @c CreateExpand.
|
|
*
|
|
* @param node_atom @c NodeAtom at the end of the edge. Used to create a node,
|
|
* unless it refers to an existing one.
|
|
* @param edge_atom @c EdgeAtom with information for the edge to be created.
|
|
* @param input Optional. Previous @c LogicalOperator which will be pulled.
|
|
* For each successful @c Cursor::Pull, this operator will create an
|
|
* expansion.
|
|
* @param input_symbol @c Symbol for the node at the start of the edge.
|
|
* @param existing_node @c bool indicating whether the @c node_atom refers to
|
|
* an existing node. If @c false, the operator will also create the node.
|
|
*/
|
|
CreateExpand(NodeAtom *node_atom, EdgeAtom *edge_atom,
|
|
const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol input_symbol, bool existing_node);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
CreateExpand() {}
|
|
|
|
class CreateExpandCursor : public Cursor {
|
|
public:
|
|
CreateExpandCursor(const CreateExpand &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const CreateExpand &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
|
|
/** Gets the existing node (if existing_node_ == true), or creates a new
|
|
* node (on the given worker) and returns it. */
|
|
VertexAccessor &OtherVertex(int worker_id, Frame &frame, Context &context);
|
|
|
|
/**
|
|
* Helper function for creating an edge and adding it
|
|
* to the frame.
|
|
*
|
|
* @param from Origin vertex of the edge.
|
|
* @param to Destination vertex of the edge.
|
|
* @param evaluator Expression evaluator for property value eval.
|
|
*/
|
|
void CreateEdge(VertexAccessor &from, VertexAccessor &to, Frame &frame,
|
|
const SymbolTable &symbol_table,
|
|
ExpressionEvaluator &evaluator);
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class scan-all (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>" :scope :protected
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(output-symbol "Symbol" :reader t :scope :protected)
|
|
(graph-view "GraphView" :reader t :scope :protected
|
|
:capnp-init nil
|
|
:capnp-save (lcp:capnp-save-enum "::query::capnp::GraphView" "GraphView"
|
|
'(old new))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::GraphView" "GraphView"
|
|
'(old new))
|
|
:documentation
|
|
"Controls which graph state is used to produce vertices.
|
|
|
|
If @c GraphView::OLD, @c ScanAll will produce vertices visible in the previous
|
|
graph state, before modifications done by current transaction & command. With
|
|
@c GraphView::NEW, all vertices will be produced the current transaction sees
|
|
along with their modifications."))
|
|
|
|
(:documentation
|
|
"Operator which iterates over all the nodes currently in the database.
|
|
When given an input (optional), does a cartesian product.
|
|
|
|
It accepts an optional input. If provided then this op scans all the nodes
|
|
currently in the database for each successful Pull from it's input, thereby
|
|
producing a cartesian product of input Pulls and database elements.
|
|
|
|
ScanAll can either iterate over the previous graph state (state before
|
|
the current transacton+command) or over current state. This is controlled
|
|
with a constructor argument.
|
|
|
|
@sa ScanAllByLabel
|
|
@sa ScanAllByLabelPropertyRange
|
|
@sa ScanAllByLabelPropertyValue")
|
|
(:public
|
|
#>cpp
|
|
ScanAll(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol,
|
|
GraphView graph_view = GraphView::OLD);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:protected #>cpp ScanAll() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class scan-all-by-label (scan-all)
|
|
((label "storage::Label" :reader t))
|
|
(:documentation
|
|
"Behaves like @c ScanAll, but this operator produces only vertices with
|
|
given label.
|
|
|
|
@sa ScanAll
|
|
@sa ScanAllByLabelPropertyRange
|
|
@sa ScanAllByLabelPropertyValue")
|
|
(:public
|
|
#>cpp
|
|
ScanAllByLabel(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol output_symbol, storage::Label label,
|
|
GraphView graph_view = GraphView::OLD);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
cpp<#)
|
|
(:private #>cpp ScanAllByLabel() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(defun save-optional-bound (builder member)
|
|
(let ((save-bound
|
|
"[helper](auto *builder, const auto &bound) {
|
|
builder->setType(bound.type() == Bound::Type::INCLUSIVE ?
|
|
::utils::capnp::Bound<::query::capnp::Tree>::Type::INCLUSIVE :
|
|
::utils::capnp::Bound<::query::capnp::Tree>::Type::EXCLUSIVE);
|
|
auto value_builder = builder->initValue();
|
|
bound.value()->Save(&value_builder, &helper->saved_ast_uids);
|
|
}"))
|
|
(funcall (lcp:capnp-save-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
|
|
save-bound)
|
|
builder member)))
|
|
|
|
(defun load-optional-bound (reader member)
|
|
(let ((load-bound
|
|
"[helper](const auto &reader) {
|
|
auto type = reader.getType() == ::utils::capnp::Bound<::query::capnp::Tree>::Type::INCLUSIVE
|
|
? Bound::Type::INCLUSIVE : Bound::Type::EXCLUSIVE;
|
|
auto *value = static_cast<Expression*>(helper->ast_storage.Load(reader.getValue(), &helper->loaded_ast_uids));
|
|
return Bound(value, type);
|
|
}"))
|
|
(funcall (lcp:capnp-load-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
|
|
load-bound)
|
|
reader member)))
|
|
|
|
(lcp:define-class scan-all-by-label-property-range (scan-all)
|
|
((label "storage::Label" :reader t)
|
|
(property "storage::Property" :reader t)
|
|
(lower-bound "std::experimental::optional<Bound>" :reader t
|
|
:capnp-type "Utils.Optional(Utils.Bound(Ast.Tree))"
|
|
:capnp-save #'save-optional-bound :capnp-load #'load-optional-bound)
|
|
(upper-bound "std::experimental::optional<Bound>" :reader t
|
|
:capnp-save #'save-optional-bound :capnp-load #'load-optional-bound
|
|
:capnp-type "Utils.Optional(Utils.Bound(Ast.Tree))"))
|
|
(:documentation
|
|
"Behaves like @c ScanAll, but produces only vertices with given label and
|
|
property value which is inside a range (inclusive or exlusive).
|
|
|
|
@sa ScanAll
|
|
@sa ScanAllByLabel
|
|
@sa ScanAllByLabelPropertyValue")
|
|
(:public
|
|
#>cpp
|
|
/** Bound with expression which when evaluated produces the bound value. */
|
|
using Bound = utils::Bound<Expression *>;
|
|
/**
|
|
* Constructs the operator for given label and property value in range
|
|
* (inclusive).
|
|
*
|
|
* Range bounds are optional, but only one bound can be left out.
|
|
*
|
|
* @param input Preceding operator which will serve as the input.
|
|
* @param output_symbol Symbol where the vertices will be stored.
|
|
* @param label Label which the vertex must have.
|
|
* @param property Property from which the value will be looked up from.
|
|
* @param lower_bound Optional lower @c Bound.
|
|
* @param upper_bound Optional upper @c Bound.
|
|
* @param graph_view GraphView used when obtaining vertices.
|
|
*/
|
|
ScanAllByLabelPropertyRange(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol output_symbol, storage::Label label,
|
|
storage::Property property,
|
|
std::experimental::optional<Bound> lower_bound,
|
|
std::experimental::optional<Bound> upper_bound,
|
|
GraphView graph_view = GraphView::OLD);
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
cpp<#)
|
|
(:private #>cpp ScanAllByLabelPropertyRange() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class scan-all-by-label-property-value (scan-all)
|
|
((label "storage::Label" :reader t)
|
|
(property "storage::Property" :reader t)
|
|
(expression "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Behaves like @c ScanAll, but produces only vertices with given label and
|
|
property value.
|
|
|
|
@sa ScanAll
|
|
@sa ScanAllByLabel
|
|
@sa ScanAllByLabelPropertyRange")
|
|
(:public
|
|
#>cpp
|
|
/**
|
|
* Constructs the operator for given label and property value.
|
|
*
|
|
* @param input Preceding operator which will serve as the input.
|
|
* @param output_symbol Symbol where the vertices will be stored.
|
|
* @param label Label which the vertex must have.
|
|
* @param property Property from which the value will be looked up from.
|
|
* @param expression Expression producing the value of the vertex property.
|
|
* @param graph_view GraphView used when obtaining vertices.
|
|
*/
|
|
ScanAllByLabelPropertyValue(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol output_symbol, storage::Label label,
|
|
storage::Property property,
|
|
Expression *expression,
|
|
GraphView graph_view = GraphView::OLD);
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
cpp<#)
|
|
(:private #>cpp ScanAllByLabelPropertyValue() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class expand-common ()
|
|
(
|
|
;; info on what's getting expanded
|
|
(node-symbol "Symbol" :reader t :scope :protected)
|
|
(edge-symbol "Symbol" :reader t :scope :protected)
|
|
(direction "EdgeAtom::Direction" :reader t :scope :protected
|
|
:capnp-type "Ast.EdgeAtom.Direction" :capnp-init nil
|
|
:capnp-save (lcp:capnp-save-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
|
|
'(in out both))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
|
|
'(in out both)))
|
|
(edge-types "std::vector<storage::EdgeType>" :reader t :scope :protected
|
|
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::EdgeType")
|
|
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::EdgeType"))
|
|
;; the input op and the symbol under which the op's result
|
|
;; can be found in the frame
|
|
(input "std::shared_ptr<LogicalOperator>" :scope :protected
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-symbol "Symbol" :reader t :scope :protected)
|
|
(existing-node :bool :reader t :scope :protected :documentation
|
|
"If the given node atom refer to a symbol that has already
|
|
been expanded and should be just validated in the frame.")
|
|
(graph-view "GraphView" :reader t :scope :protected
|
|
:capnp-init nil
|
|
:capnp-save (lcp:capnp-save-enum "::query::capnp::GraphView" "GraphView"
|
|
'(old new))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::GraphView" "GraphView"
|
|
'(old new))
|
|
:documentation
|
|
"from which state the input node should get expanded"))
|
|
(:documentation
|
|
"Common functionality and data members of single-edge and variable-length
|
|
expansion")
|
|
(:public
|
|
#>cpp
|
|
/**
|
|
* Initializes common expansion parameters.
|
|
*
|
|
* Edge/Node existence are controlled with booleans. 'true'
|
|
* denotes that this expansion references an already
|
|
* Pulled node/edge, and should only be checked for equality
|
|
* during expansion.
|
|
*
|
|
* Expansion can be done from old or new state of the vertex
|
|
* the expansion originates from. This is controlled with a
|
|
* constructor argument.
|
|
*
|
|
* @param node_symbol Symbol pointing to the node to be expanded. This is
|
|
* where the new node will be stored.
|
|
* @param edge_symbol Symbol for the edges to be expanded. This is where
|
|
* a TypedValue containing a list of expanded edges will be stored.
|
|
* @param direction EdgeAtom::Direction determining the direction of edge
|
|
* expansion. The direction is relative to the starting vertex for each
|
|
* expansion.
|
|
* @param edge_types storage::EdgeType specifying which edges we
|
|
* want to expand. If empty, all edges are valid. If not empty, only edges
|
|
* with one of the given types are valid.
|
|
* @param input Optional LogicalOperator that preceeds this one.
|
|
* @param input_symbol Symbol that points to a VertexAccessor in the Frame
|
|
* that expansion should emanate from.
|
|
* @param existing_node If or not the node to be expanded is already present
|
|
* in the Frame and should just be checked for equality.
|
|
*/
|
|
ExpandCommon(Symbol node_symbol, Symbol edge_symbol,
|
|
EdgeAtom::Direction direction,
|
|
const std::vector<storage::EdgeType> &edge_types,
|
|
const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol input_symbol, bool existing_node,
|
|
GraphView graph_view);
|
|
cpp<#)
|
|
(:protected
|
|
#>cpp
|
|
virtual ~ExpandCommon() {}
|
|
|
|
// types that we'll use for members in both subclasses
|
|
using InEdgeT = decltype(std::declval<VertexAccessor>().in());
|
|
using InEdgeIteratorT = decltype(std::declval<VertexAccessor>().in().begin());
|
|
using OutEdgeT = decltype(std::declval<VertexAccessor>().out());
|
|
using OutEdgeIteratorT =
|
|
decltype(std::declval<VertexAccessor>().out().begin());
|
|
|
|
/**
|
|
* For a newly expanded node handles existence checking and
|
|
* frame placement.
|
|
*
|
|
* @return If or not the given new_node is a valid expansion. It is not
|
|
* valid only when matching and existing node and new_node does not match
|
|
* the old.
|
|
*/
|
|
bool HandleExistingNode(const VertexAccessor &new_node, Frame &frame) const;
|
|
|
|
ExpandCommon() {}
|
|
cpp<#)
|
|
(:serialize :capnp
|
|
:save-args '((helper "LogicalOperator::SaveHelper *"))
|
|
:load-args '((helper "LogicalOperator::LoadHelper *"))))
|
|
|
|
(lcp:define-class expand (logical-operator expand-common)
|
|
()
|
|
(:documentation
|
|
"Expansion operator. For a node existing in the frame it
|
|
expands one edge and one node and places them on the frame.
|
|
|
|
This class does not handle node/edge filtering based on
|
|
properties, labels and edge types. However, it does handle
|
|
filtering on existing node / edge.
|
|
|
|
Filtering on existing means that for a pattern that references
|
|
an already declared node or edge (for example in
|
|
MATCH (a) MATCH (a)--(b)),
|
|
only expansions that match defined equalities are successfully
|
|
pulled.")
|
|
(:public
|
|
#>cpp
|
|
/**
|
|
* Creates an expansion. All parameters are forwarded to @c ExpandCommon and
|
|
* are documented there.
|
|
*/
|
|
using ExpandCommon::ExpandCommon;
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
|
|
class ExpandCursor : public Cursor {
|
|
public:
|
|
ExpandCursor(const Expand &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
struct FutureExpand {
|
|
utils::Future<std::pair<EdgeAccessor, VertexAccessor>> edge_to;
|
|
std::vector<TypedValue> frame_elems;
|
|
};
|
|
|
|
const Expand &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
database::GraphDbAccessor &db_;
|
|
|
|
// The iterable over edges and the current edge iterator are referenced via
|
|
// optional because they can not be initialized in the constructor of
|
|
// this class. They are initialized once for each pull from the input.
|
|
std::experimental::optional<InEdgeT> in_edges_;
|
|
std::experimental::optional<InEdgeIteratorT> in_edges_it_;
|
|
std::experimental::optional<OutEdgeT> out_edges_;
|
|
std::experimental::optional<OutEdgeIteratorT> out_edges_it_;
|
|
// Stores the last frame before we yield the frame for future edge. It needs
|
|
// to be restored afterward.
|
|
std::vector<TypedValue> last_frame_;
|
|
// Edges which are being asynchronously fetched from a remote worker.
|
|
// NOTE: This should be destructed first to ensure that no invalid
|
|
// references or pointers exists to other objects of this class.
|
|
std::vector<FutureExpand> future_expands_;
|
|
|
|
bool InitEdges(Frame &, Context &);
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp :inherit-compose '(expand-common)))
|
|
|
|
(lcp:define-class expand-variable (logical-operator expand-common)
|
|
((type "EdgeAtom::Type" :reader t :capnp-type "Ast.EdgeAtom.Type"
|
|
:capnp-init nil
|
|
:capnp-save (lcp:capnp-save-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
|
|
'(single depth-first breadth-first weighted-shortest-path))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
|
|
'(single depth-first breadth-first weighted-shortest-path)))
|
|
(is-reverse :bool :reader t :documentation
|
|
"True if the path should be written as expanding from node_symbol to input_symbol.")
|
|
(lower-bound "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
|
:documentation "Optional lower bound of the variable length expansion, defaults are (1, inf)")
|
|
(upper-bound "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
|
|
:documentation "Optional upper bound of the variable length expansion, defaults are (1, inf)")
|
|
(filter-lambda "Lambda" :reader t)
|
|
(weight-lambda "std::experimental::optional<Lambda>" :reader t
|
|
:capnp-save (lcp:capnp-save-optional
|
|
"capnp::ExpandVariable::Lambda" "Lambda"
|
|
"[helper](auto *builder, const auto &val) { val.Save(builder, helper); }")
|
|
:capnp-load (lcp:capnp-load-optional
|
|
"capnp::ExpandVariable::Lambda" "Lambda"
|
|
"[helper](const auto &reader) { Lambda val; val.Load(reader, helper); return val; }"))
|
|
(total-weight "std::experimental::optional<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-optional "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-optional "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Variable-length expansion operator. For a node existing in
|
|
the frame it expands a variable number of edges and places them
|
|
(in a list-type TypedValue), as well as the final destination node,
|
|
on the frame.
|
|
|
|
This class does not handle node/edge filtering based on
|
|
properties, labels and edge types. However, it does handle
|
|
filtering on existing node / edge. Additionally it handles's
|
|
edge-uniquess (cyphermorphism) because it's not feasable to do
|
|
later.
|
|
|
|
Filtering on existing means that for a pattern that references
|
|
an already declared node or edge (for example in
|
|
MATCH (a) MATCH (a)--(b)),
|
|
only expansions that match defined equalities are succesfully
|
|
pulled.")
|
|
(:public
|
|
(lcp:define-struct lambda ()
|
|
((inner-edge-symbol "Symbol" :documentation "Currently expanded edge symbol.")
|
|
(inner-node-symbol "Symbol" :documentation "Currently expanded node symbol.")
|
|
(expression "Expression *" :documentation "Expression used in lambda during expansion."
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:serialize :capnp
|
|
:save-args '((helper "LogicalOperator::SaveHelper *"))
|
|
:load-args '((helper "LogicalOperator::LoadHelper *"))))
|
|
#>cpp
|
|
/**
|
|
* Creates a variable-length expansion. Most params are forwarded
|
|
* to the @c ExpandCommon constructor, and are documented there.
|
|
*
|
|
* Expansion length bounds are both inclusive (as in Neo's Cypher
|
|
* implementation).
|
|
*
|
|
* @param type - Either Type::DEPTH_FIRST (default variable-length expansion),
|
|
* or Type::BREADTH_FIRST.
|
|
* @param is_reverse Set to `true` if the edges written on frame should expand
|
|
* from `node_symbol` to `input_symbol`. Opposed to the usual expanding
|
|
* from `input_symbol` to `node_symbol`.
|
|
* @param lower_bound An optional indicator of the minimum number of edges
|
|
* that get expanded (inclusive).
|
|
* @param upper_bound An optional indicator of the maximum number of edges
|
|
* that get expanded (inclusive).
|
|
* @param inner_edge_symbol Like `inner_node_symbol`
|
|
* @param inner_node_symbol For each expansion the node expanded into is
|
|
* assigned to this symbol so it can be evaulated by the 'where'
|
|
* expression.
|
|
* @param filter_ The filter that must be satisfied for an expansion to
|
|
* succeed. Can use inner(node/edge) symbols. If nullptr, it is ignored.
|
|
*/
|
|
ExpandVariable(Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Type type,
|
|
EdgeAtom::Direction direction,
|
|
const std::vector<storage::EdgeType> &edge_types,
|
|
bool is_reverse, Expression *lower_bound,
|
|
Expression *upper_bound,
|
|
const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol input_symbol, bool existing_node, Lambda filter_lambda,
|
|
std::experimental::optional<Lambda> weight_lambda,
|
|
std::experimental::optional<Symbol> total_weight,
|
|
GraphView graph_view);
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
// the Cursors are not declared in the header because
|
|
// it's edges_ and edges_it_ are decltyped using a helper function
|
|
// that should be inaccessible (private class function won't compile)
|
|
friend class ExpandVariableCursor;
|
|
friend class ExpandWeightedShortestPathCursor;
|
|
|
|
ExpandVariable() {}
|
|
cpp<#)
|
|
(:serialize :capnp :inherit-compose '(expand-common)))
|
|
|
|
(lcp:define-class construct-named-path (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(path-symbol "Symbol" :reader t)
|
|
(path-elements "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Constructs a named path from it's elements and places it on the frame.")
|
|
(:public
|
|
#>cpp
|
|
ConstructNamedPath(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol path_symbol,
|
|
const std::vector<Symbol> &path_elements)
|
|
: input_(input),
|
|
path_symbol_(path_symbol),
|
|
path_elements_(path_elements) {}
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp ConstructNamedPath() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class filter (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(expression "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Filter whose Pull returns true only when the given expression
|
|
evaluates into true.
|
|
|
|
The given expression is assumed to return either NULL (treated as false) or
|
|
a boolean value.")
|
|
(:public
|
|
#>cpp
|
|
Filter(const std::shared_ptr<LogicalOperator> &input_,
|
|
Expression *expression_);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Filter() {}
|
|
|
|
class FilterCursor : public Cursor {
|
|
public:
|
|
FilterCursor(const Filter &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Filter &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class produce (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(named-expressions "std::vector<NamedExpression *>" :reader t
|
|
:capnp-type "List(Ast.Tree)"
|
|
:capnp-save (save-ast-vector "NamedExpression *")
|
|
:capnp-load (load-ast-vector "NamedExpression *")))
|
|
(:documentation
|
|
"A logical operator that places an arbitrary number
|
|
if named expressions on the frame (the logical operator
|
|
for the RETURN clause).
|
|
|
|
Supports optional input. When the input is provided,
|
|
it is Pulled from and the Produce succeeds once for
|
|
every input Pull (typically a MATCH/RETURN query).
|
|
When the input is not provided (typically a standalone
|
|
RETURN clause) the Produce's pull succeeds exactly once.")
|
|
(:public
|
|
#>cpp
|
|
Produce(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::vector<NamedExpression *> &named_expressions);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
class ProduceCursor : public Cursor {
|
|
public:
|
|
ProduceCursor(const Produce &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Produce &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
|
|
Produce() {}
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class delete (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(expressions "std::vector<Expression *>" :reader t
|
|
:capnp-type "List(Ast.Tree)"
|
|
:capnp-save (save-ast-vector "Expression *")
|
|
:capnp-load (load-ast-vector "Expression *"))
|
|
(detach :bool :documentation
|
|
"if the vertex should be detached before deletion if not detached,
|
|
and has connections, an error is raised ignored when deleting edges"))
|
|
(:documentation
|
|
"Operator for deleting vertices and edges.
|
|
|
|
Has a flag for using DETACH DELETE when deleting vertices.")
|
|
(:public
|
|
#>cpp
|
|
Delete(const std::shared_ptr<LogicalOperator> &input_,
|
|
const std::vector<Expression *> &expressions, bool detach_);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Delete() {}
|
|
|
|
class DeleteCursor : public Cursor {
|
|
public:
|
|
DeleteCursor(const Delete &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Delete &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class set-property (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(lhs "PropertyLookup *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *"))
|
|
(rhs "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Logical Op for setting a single property on a single vertex or edge.
|
|
|
|
The property value is an expression that must evaluate to some type that
|
|
can be stored (a TypedValue that can be converted to PropertyValue).")
|
|
(:public
|
|
#>cpp
|
|
SetProperty(const std::shared_ptr<LogicalOperator> &input,
|
|
PropertyLookup *lhs, Expression *rhs);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
SetProperty() {}
|
|
|
|
class SetPropertyCursor : public Cursor {
|
|
public:
|
|
SetPropertyCursor(const SetProperty &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const SetProperty &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class set-properties (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-symbol "Symbol" :reader t)
|
|
(rhs "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *"))
|
|
(op "Op"))
|
|
(:documentation
|
|
"Logical op for setting the whole properties set on a vertex or an edge.
|
|
|
|
The value being set is an expression that must evaluate to a vertex, edge
|
|
or map (literal or parameter).
|
|
|
|
Supports setting (replacing the whole properties set with another) and
|
|
updating.")
|
|
(:public
|
|
(lcp:define-enum op
|
|
(update replace)
|
|
(:documentation "Defines how setting the properties works.
|
|
|
|
@c UPDATE means that the current property set is augmented with additional
|
|
ones (existing props of the same name are replaced), while @c REPLACE means
|
|
that the old props are discarded and replaced with new ones.")
|
|
(:serialize :capnp))
|
|
|
|
#>cpp
|
|
SetProperties(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol input_symbol, Expression *rhs, Op op);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
SetProperties() {}
|
|
|
|
class SetPropertiesCursor : public Cursor {
|
|
public:
|
|
SetPropertiesCursor(const SetProperties &self,
|
|
database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const SetProperties &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
|
|
/** Helper function that sets the given values on either
|
|
* a VertexRecord or an EdgeRecord.
|
|
* @tparam TRecordAccessor Either RecordAccessor<Vertex> or
|
|
* RecordAccessor<Edge>
|
|
*/
|
|
template <typename TRecordAccessor>
|
|
void Set(TRecordAccessor &record, const TypedValue &rhs) const;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class set-labels (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-symbol "Symbol" :reader t)
|
|
(labels "std::vector<storage::Label>"
|
|
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
|
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
|
(:documentation
|
|
"Logical operator for setting an arbitrary number of labels on a Vertex.
|
|
|
|
It does NOT remove labels that are already set on that Vertex.")
|
|
(:public
|
|
#>cpp
|
|
SetLabels(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
|
|
const std::vector<storage::Label> &labels);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
SetLabels() {}
|
|
|
|
class SetLabelsCursor : public Cursor {
|
|
public:
|
|
SetLabelsCursor(const SetLabels &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const SetLabels &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class remove-property (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(lhs "PropertyLookup *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "PropertyLookup *")))
|
|
(:documentation
|
|
"Logical op for removing a property from an edge or a vertex.")
|
|
(:public
|
|
#>cpp
|
|
RemoveProperty(const std::shared_ptr<LogicalOperator> &input,
|
|
PropertyLookup *lhs);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
RemoveProperty() {}
|
|
|
|
class RemovePropertyCursor : public Cursor {
|
|
public:
|
|
RemovePropertyCursor(const RemoveProperty &self,
|
|
database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const RemoveProperty &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class remove-labels (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-symbol "Symbol" :reader t)
|
|
(labels "std::vector<storage::Label>"
|
|
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
|
|
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
|
|
(:documentation
|
|
"Logical operator for removing an arbitrary number of labels on a Vertex.
|
|
|
|
If a label does not exist on a Vertex, nothing happens.")
|
|
(:public
|
|
#>cpp
|
|
RemoveLabels(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol input_symbol, const std::vector<storage::Label> &labels);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
RemoveLabels() {}
|
|
|
|
class RemoveLabelsCursor : public Cursor {
|
|
public:
|
|
RemoveLabelsCursor(const RemoveLabels &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const RemoveLabels &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class (expand-uniqueness-filter t-accessor) (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(expand-symbol "Symbol" :reader t)
|
|
(previous-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Filter whose Pull returns true only when the given
|
|
expand_symbol frame value (the latest expansion) is not
|
|
equal to any of the previous_symbols frame values.
|
|
|
|
Used for implementing [iso/cypher]morphism.
|
|
Isomorphism is vertex-uniqueness. It means that
|
|
two different vertices in a pattern can not map to the
|
|
same data vertex. For example, if the database
|
|
contains one vertex with a recursive relationship,
|
|
then the query
|
|
MATCH ()-[]->() combined with vertex uniqueness
|
|
yields no results (no uniqueness yields one).
|
|
Cyphermorphism is edge-uniqueness (the above
|
|
explanation applies). By default Neo4j uses
|
|
Cyphermorphism (that's where the name stems from,
|
|
it is not a valid graph-theory term).
|
|
|
|
Works for both Edge and Vertex uniqueness checks
|
|
(provide the accessor type as a template argument).
|
|
Supports variable-length-edges (uniqueness comparisons
|
|
between edges and an edge lists).")
|
|
(:public
|
|
#>cpp
|
|
ExpandUniquenessFilter(const std::shared_ptr<LogicalOperator> &input,
|
|
Symbol expand_symbol,
|
|
const std::vector<Symbol> &previous_symbols);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
ExpandUniquenessFilter() {}
|
|
|
|
class ExpandUniquenessFilterCursor : public Cursor {
|
|
public:
|
|
ExpandUniquenessFilterCursor(const ExpandUniquenessFilter &self,
|
|
database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const ExpandUniquenessFilter &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp :type-args '(vertex-accessor edge-accessor)))
|
|
|
|
(lcp:define-class accumulate (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
|
|
(advance-command :bool :reader t))
|
|
(:documentation
|
|
"Pulls everything from the input before passing it through.
|
|
Optionally advances the command after accumulation and before emitting.
|
|
|
|
On the first Pull from this Op's Cursor the input Cursor will be
|
|
Pulled until it is empty. The results will be accumulated in the
|
|
temporary cache. Once the input Cursor is empty, this Op's Cursor
|
|
will start returning cached stuff from it's Pull.
|
|
|
|
This technique is used for ensuring all the operations from the
|
|
previous LogicalOp have been performed before exposing data
|
|
to the next. A typical use-case is the `MATCH - SET - RETURN`
|
|
query in which every SET iteration must be performed before
|
|
RETURN starts iterating (see Memgraph Wiki for detailed reasoning).
|
|
|
|
IMPORTANT: This Op does not cache all the results but only those
|
|
elements from the frame whose symbols (frame positions) it was given.
|
|
All other frame positions will contain undefined junk after this
|
|
op has executed, and should not be used.
|
|
|
|
This op can also advance the command after the accumulation and
|
|
before emitting. If the command gets advanced, every value that
|
|
has been cached will be reconstructed before Pull returns.
|
|
|
|
@param input Input @c LogicalOperator.
|
|
@param symbols A vector of Symbols that need to be accumulated
|
|
and exposed to the next op.")
|
|
(:public
|
|
#>cpp
|
|
Accumulate(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::vector<Symbol> &symbols, bool advance_command = false);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Accumulate() {}
|
|
|
|
class AccumulateCursor : public Cursor {
|
|
public:
|
|
AccumulateCursor(const Accumulate &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Accumulate &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
std::vector<std::vector<TypedValue>> cache_;
|
|
decltype(cache_.begin()) cache_it_ = cache_.begin();
|
|
bool pulled_all_input_{false};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
#>cpp
|
|
/**
|
|
* Custom equality function for a vector of typed values.
|
|
* Used in unordered_maps in Aggregate and Distinct operators.
|
|
*/
|
|
struct TypedValueVectorEqual {
|
|
bool operator()(const std::vector<TypedValue> &left,
|
|
const std::vector<TypedValue> &right) const;
|
|
};
|
|
cpp<#
|
|
|
|
(lcp:define-class aggregate (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(aggregations "std::vector<Element>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector
|
|
"capnp::Aggregate::Element" "Element"
|
|
"[helper](auto *builder, const auto &val) { val.Save(builder, helper); }")
|
|
:capnp-load (lcp:capnp-load-vector
|
|
"capnp::Aggregate::Element" "Element"
|
|
"[helper](const auto &reader) { Element val; val.Load(reader, helper); return val; }"))
|
|
(group-by "std::vector<Expression *>" :reader t
|
|
:capnp-type "List(Ast.Tree)"
|
|
:capnp-save (save-ast-vector "Expression *")
|
|
:capnp-load (load-ast-vector "Expression *"))
|
|
(remember "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Performs an arbitrary number of aggregations of data
|
|
from the given input grouped by the given criteria.
|
|
|
|
Aggregations are defined by triples that define
|
|
(input data expression, type of aggregation, output symbol).
|
|
Input data is grouped based on the given set of named
|
|
expressions. Grouping is done on unique values.
|
|
|
|
IMPORTANT:
|
|
Ops taking their input from an aggregation are only
|
|
allowed to use frame values that are either aggregation
|
|
outputs or group-by named-expressions. All other frame
|
|
elements are in an undefined state after aggregation.")
|
|
(:public
|
|
(lcp:define-struct element ()
|
|
((value "Expression *"
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *"))
|
|
(key "Expression *"
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *"))
|
|
(op "Aggregation::Op" :capnp-type "Ast.Aggregation.Op"
|
|
:capnp-init nil :capnp-save (lcp:capnp-save-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
|
|
'(count min max sum avg collect-list collect-map))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
|
|
'(count min max sum avg collect-list collect-map)))
|
|
(output-sym "Symbol"))
|
|
(:documentation
|
|
"An aggregation element, contains:
|
|
(input data expression, key expression - only used in COLLECT_MAP, type of
|
|
aggregation, output symbol).")
|
|
(:serialize :capnp
|
|
:save-args '((helper "LogicalOperator::SaveHelper *"))
|
|
:load-args '((helper "LogicalOperator::LoadHelper *"))))
|
|
#>cpp
|
|
Aggregate(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::vector<Element> &aggregations,
|
|
const std::vector<Expression *> &group_by,
|
|
const std::vector<Symbol> &remember);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Aggregate() {}
|
|
|
|
class AggregateCursor : public Cursor {
|
|
public:
|
|
AggregateCursor(const Aggregate &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
// Data structure for a single aggregation cache.
|
|
// does NOT include the group-by values since those
|
|
// are a key in the aggregation map.
|
|
// The vectors in an AggregationValue contain one element
|
|
// for each aggregation in this LogicalOp.
|
|
struct AggregationValue {
|
|
// how many input rows has been aggregated in respective
|
|
// values_ element so far
|
|
std::vector<int> counts_;
|
|
// aggregated values. Initially Null (until at least one
|
|
// input row with a valid value gets processed)
|
|
std::vector<TypedValue> values_;
|
|
// remember values.
|
|
std::vector<TypedValue> remember_;
|
|
};
|
|
|
|
const Aggregate &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
// storage for aggregated data
|
|
// map key is the vector of group-by values
|
|
// map value is an AggregationValue struct
|
|
std::unordered_map<
|
|
std::vector<TypedValue>, AggregationValue,
|
|
// use FNV collection hashing specialized for a vector of TypedValues
|
|
utils::FnvCollection<std::vector<TypedValue>, TypedValue,
|
|
TypedValue::Hash>,
|
|
// custom equality
|
|
TypedValueVectorEqual>
|
|
aggregation_;
|
|
// iterator over the accumulated cache
|
|
decltype(aggregation_.begin()) aggregation_it_ = aggregation_.begin();
|
|
// this LogicalOp pulls all from the input on it's first pull
|
|
// this switch tracks if this has been performed
|
|
bool pulled_all_input_{false};
|
|
|
|
/**
|
|
* Pulls from the input operator until exhausted and aggregates the
|
|
* results. If the input operator is not provided, a single call
|
|
* to ProcessOne is issued.
|
|
*
|
|
* Accumulation automatically groups the results so that `aggregation_`
|
|
* cache cardinality depends on number of
|
|
* aggregation results, and not on the number of inputs.
|
|
*/
|
|
void ProcessAll(Frame &, Context &);
|
|
|
|
/**
|
|
* Performs a single accumulation.
|
|
*/
|
|
void ProcessOne(Frame &frame, const SymbolTable &symbolTable,
|
|
ExpressionEvaluator &evaluator);
|
|
|
|
/** Ensures the new AggregationValue has been initialized. This means
|
|
* that the value vectors are filled with an appropriate number of Nulls,
|
|
* counts are set to 0 and remember values are remembered.
|
|
*/
|
|
void EnsureInitialized(Frame &frame, AggregationValue &agg_value) const;
|
|
|
|
/** Updates the given AggregationValue with new data. Assumes that
|
|
* the AggregationValue has been initialized */
|
|
void Update(Frame &frame, const SymbolTable &symbol_table,
|
|
ExpressionEvaluator &evaluator, AggregationValue &agg_value);
|
|
|
|
/** Checks if the given TypedValue is legal in MIN and MAX. If not
|
|
* an appropriate exception is thrown. */
|
|
void EnsureOkForMinMax(const TypedValue &value) const;
|
|
|
|
/** Checks if the given TypedValue is legal in AVG and SUM. If not
|
|
* an appropriate exception is thrown. */
|
|
void EnsureOkForAvgSum(const TypedValue &value) const;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class skip (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(expression "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Skips a number of Pulls from the input op.
|
|
|
|
The given expression determines how many Pulls from the input
|
|
should be skipped (ignored).
|
|
All other successful Pulls from the
|
|
input are simply passed through.
|
|
|
|
The given expression is evaluated after the first Pull from
|
|
the input, and only once. Neo does not allow this expression
|
|
to contain identifiers, and neither does Memgraph, but this
|
|
operator's implementation does not expect this.")
|
|
(:public
|
|
#>cpp
|
|
Skip(const std::shared_ptr<LogicalOperator> &input, Expression *expression);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Skip() {}
|
|
|
|
class SkipCursor : public Cursor {
|
|
public:
|
|
SkipCursor(const Skip &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Skip &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
// init to_skip_ to -1, indicating
|
|
// that it's still unknown (input has not been Pulled yet)
|
|
int to_skip_{-1};
|
|
int skipped_{0};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class limit (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(expression "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Limits the number of Pulls from the input op.
|
|
|
|
The given expression determines how many
|
|
input Pulls should be passed through. The input is not
|
|
Pulled once this limit is reached. Note that this has
|
|
implications: the out-of-bounds input Pulls are never
|
|
evaluated.
|
|
|
|
The limit expression must NOT use anything from the
|
|
Frame. It is evaluated before the first Pull from the
|
|
input. This is consistent with Neo (they don't allow
|
|
identifiers in limit expressions), and it's necessary
|
|
when limit evaluates to 0 (because 0 Pulls from the
|
|
input should be performed).")
|
|
(:public
|
|
#>cpp
|
|
Limit(const std::shared_ptr<LogicalOperator> &input, Expression *expression);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Limit() {}
|
|
|
|
class LimitCursor : public Cursor {
|
|
public:
|
|
LimitCursor(const Limit &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Limit &self_;
|
|
std::unique_ptr<Cursor> input_cursor_;
|
|
// init limit_ to -1, indicating
|
|
// that it's still unknown (Cursor has not been Pulled yet)
|
|
int limit_{-1};
|
|
int pulled_{0};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class order-by (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(compare "TypedValueVectorCompare" :reader t
|
|
:capnp-type "Common.TypedValueVectorCompare")
|
|
(order-by "std::vector<Expression *>" :reader t
|
|
:capnp-type "List(Ast.Tree)"
|
|
:capnp-save (save-ast-vector "Expression *")
|
|
:capnp-load (load-ast-vector "Expression *"))
|
|
(output-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Logical operator for ordering (sorting) results.
|
|
|
|
Sorts the input rows based on an arbitrary number of
|
|
Expressions. Ascending or descending ordering can be chosen
|
|
for each independently (not providing enough orderings
|
|
results in a runtime error).
|
|
|
|
For each row an arbitrary number of Frame elements can be
|
|
remembered. Only these elements (defined by their Symbols)
|
|
are valid for usage after the OrderBy operator.")
|
|
(:public
|
|
#>cpp
|
|
OrderBy(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::vector<std::pair<Ordering, Expression *>> &order_by,
|
|
const std::vector<Symbol> &output_symbols);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
OrderBy() {}
|
|
|
|
class OrderByCursor : public Cursor {
|
|
public:
|
|
OrderByCursor(const OrderBy &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const OrderBy &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
bool did_pull_all_{false};
|
|
// a cache of elements pulled from the input
|
|
// first pair element is the order-by vector
|
|
// second pair is the remember vector
|
|
// the cache is filled and sorted (only on first pair elem) on first Pull
|
|
std::vector<std::pair<std::vector<TypedValue>, std::vector<TypedValue>>>
|
|
cache_;
|
|
// iterator over the cache_, maintains state between Pulls
|
|
decltype(cache_.begin()) cache_it_ = cache_.begin();
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class merge (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(merge-match "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(merge-create "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer))
|
|
(:documentation
|
|
"Merge operator. For every sucessful Pull from the
|
|
input operator a Pull from the merge_match is attempted. All
|
|
successfull Pulls from the merge_match are passed on as output.
|
|
If merge_match Pull does not yield any elements, a single Pull
|
|
from the merge_create op is performed.
|
|
|
|
The input logical op is optional. If false (nullptr)
|
|
it will be replaced by a Once op.
|
|
|
|
For an argumentation of this implementation see the wiki
|
|
documentation.")
|
|
(:public
|
|
#>cpp
|
|
Merge(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::shared_ptr<LogicalOperator> &merge_match,
|
|
const std::shared_ptr<LogicalOperator> &merge_create);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
// TODO: Consider whether we want to treat Merge as having single input. It
|
|
// makes sense that we do, because other branches are executed depending on
|
|
// the input.
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Merge() {}
|
|
|
|
class MergeCursor : public Cursor {
|
|
public:
|
|
MergeCursor(const Merge &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
const std::unique_ptr<Cursor> merge_match_cursor_;
|
|
const std::unique_ptr<Cursor> merge_create_cursor_;
|
|
|
|
// indicates if the next Pull from this cursor
|
|
// should perform a pull from input_cursor_
|
|
// this is true when:
|
|
// - first Pulling from this cursor
|
|
// - previous Pull from this cursor exhausted the merge_match_cursor
|
|
bool pull_input_{true};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class optional (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(optional "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(optional-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Optional operator. Used for optional match. For every
|
|
successful Pull from the input branch a Pull from the optional
|
|
branch is attempted (and Pulled from till exhausted). If zero
|
|
Pulls succeed from the optional branch, the Optional operator
|
|
sets the optional symbols to TypedValue::Null on the Frame
|
|
and returns true, once.")
|
|
(:public
|
|
#>cpp
|
|
Optional(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::shared_ptr<LogicalOperator> &optional,
|
|
const std::vector<Symbol> &optional_symbols);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Optional() {}
|
|
|
|
class OptionalCursor : public Cursor {
|
|
public:
|
|
OptionalCursor(const Optional &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Optional &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
const std::unique_ptr<Cursor> optional_cursor_;
|
|
// indicates if the next Pull from this cursor should
|
|
// perform a Pull from the input_cursor_
|
|
// this is true when:
|
|
// - first pulling from this Cursor
|
|
// - previous Pull from this cursor exhausted the optional_cursor_
|
|
bool pull_input_{true};
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class auth-handler (logical-operator)
|
|
((action "AuthQuery::Action" :reader t
|
|
:capnp-init nil
|
|
:capnp-type "Ast.AuthQuery.Action"
|
|
:capnp-save (lcp:capnp-save-enum "::query::capnp::AuthQuery::Action"
|
|
"AuthQuery::Action"
|
|
'(create-role drop-role show-roles
|
|
create-user set-password
|
|
drop-user show-users set-role
|
|
clear-role grant-privilege
|
|
deny-privilege revoke-privilege
|
|
show-privileges show-role-for-user
|
|
show-users-for-role))
|
|
:capnp-load (lcp:capnp-load-enum "::query::capnp::AuthQuery::Action"
|
|
"AuthQuery::Action"
|
|
'(create-role drop-role show-roles
|
|
create-user set-password
|
|
drop-user show-users set-role
|
|
clear-role grant-privilege
|
|
deny-privilege revoke-privilege
|
|
show-privileges show-role-for-user
|
|
show-users-for-role)))
|
|
(user "std::string" :reader t)
|
|
(role "std::string" :reader t)
|
|
(user-or-role "std::string" :reader t)
|
|
(password "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(privileges "std::vector<AuthQuery::Privilege>" :reader t
|
|
:capnp-type "List(Ast.AuthQuery.Privilege)"
|
|
:capnp-save
|
|
(lambda (builder member-name)
|
|
#>cpp
|
|
for (size_t i = 0; i < ${member-name}.size(); ++i) {
|
|
switch (privileges_[i]) {
|
|
case AuthQuery::Privilege::CREATE:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::CREATE);
|
|
break;
|
|
case AuthQuery::Privilege::DELETE:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::DELETE);
|
|
break;
|
|
case AuthQuery::Privilege::MATCH:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::MATCH);
|
|
break;
|
|
case AuthQuery::Privilege::MERGE:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::MERGE);
|
|
break;
|
|
case AuthQuery::Privilege::SET:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::SET);
|
|
break;
|
|
case AuthQuery::Privilege::REMOVE:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::REMOVE);
|
|
break;
|
|
case AuthQuery::Privilege::INDEX:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::INDEX);
|
|
break;
|
|
case AuthQuery::Privilege::AUTH:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::AUTH);
|
|
break;
|
|
case AuthQuery::Privilege::STREAM:
|
|
${builder}.set(i, query::capnp::AuthQuery::Privilege::STREAM);
|
|
break;
|
|
}
|
|
}
|
|
cpp<#)
|
|
:capnp-load
|
|
(lambda (reader member-name)
|
|
#>cpp
|
|
for (auto privilege : ${reader}) {
|
|
switch (privilege) {
|
|
case query::capnp::AuthQuery::Privilege::CREATE:
|
|
${member-name}.push_back(AuthQuery::Privilege::CREATE);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::DELETE:
|
|
${member-name}.push_back(AuthQuery::Privilege::DELETE);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::MATCH:
|
|
${member-name}.push_back(AuthQuery::Privilege::MATCH);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::MERGE:
|
|
${member-name}.push_back(AuthQuery::Privilege::MERGE);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::SET:
|
|
${member-name}.push_back(AuthQuery::Privilege::SET);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::REMOVE:
|
|
${member-name}.push_back(AuthQuery::Privilege::REMOVE);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::INDEX:
|
|
${member-name}.push_back(AuthQuery::Privilege::INDEX);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::AUTH:
|
|
${member-name}.push_back(AuthQuery::Privilege::AUTH);
|
|
break;
|
|
case query::capnp::AuthQuery::Privilege::STREAM:
|
|
${member-name}.push_back(AuthQuery::Privilege::STREAM);
|
|
break;
|
|
}
|
|
}
|
|
cpp<#))
|
|
(user-symbol "Symbol" :reader t)
|
|
(role-symbol "Symbol" :reader t)
|
|
(privilege-symbol "Symbol" :reader t)
|
|
(effective-symbol "Symbol" :reader t)
|
|
(details-symbol "Symbol" :reader t))
|
|
(:public
|
|
#>cpp
|
|
AuthHandler(AuthQuery::Action action, std::string user, std::string role,
|
|
std::string user_or_role, Expression * password,
|
|
std::vector<AuthQuery::Privilege> privileges,
|
|
Symbol user_symbol, Symbol role_symbol,
|
|
Symbol privilege_symbol, Symbol effective_symbol,
|
|
Symbol details_symbol);
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(database::GraphDbAccessor & db)
|
|
const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
virtual std::vector<Symbol> ModifiedSymbols(const SymbolTable &)
|
|
const override { return {}; }
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:protected #>cpp AuthHandler() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class unwind (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(input-expression "Expression *" :reader t
|
|
:capnp-type "Ast.Tree" :capnp-init nil
|
|
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *"))
|
|
(output-symbol "Symbol"))
|
|
(:documentation
|
|
"Takes a list TypedValue as it's input and yields each
|
|
element as it's output.
|
|
|
|
Input is optional (unwind can be the first clause in a query).")
|
|
(:public
|
|
#>cpp
|
|
Unwind(const std::shared_ptr<LogicalOperator> &input,
|
|
Expression *input_expression_, Symbol output_symbol);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override {
|
|
return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override {
|
|
return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Unwind() {}
|
|
|
|
class UnwindCursor : public Cursor {
|
|
public:
|
|
UnwindCursor(const Unwind &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Unwind &self_;
|
|
database::GraphDbAccessor &db_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
// typed values we are unwinding and yielding
|
|
std::vector<TypedValue> input_value_;
|
|
// current position in input_value_
|
|
std::vector<TypedValue>::iterator input_value_it_ = input_value_.end();
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class distinct (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(value-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Ensures that only distinct rows are yielded.
|
|
This implementation accepts a vector of Symbols
|
|
which define a row. Only those Symbols are valid
|
|
for use in operators following Distinct.
|
|
|
|
This implementation maintains input ordering.")
|
|
(:public
|
|
#>cpp
|
|
Distinct(const std::shared_ptr<LogicalOperator> &input,
|
|
const std::vector<Symbol> &value_symbols);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Distinct() {}
|
|
|
|
class DistinctCursor : public Cursor {
|
|
public:
|
|
DistinctCursor(const Distinct &self, database::GraphDbAccessor &db);
|
|
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Distinct &self_;
|
|
const std::unique_ptr<Cursor> input_cursor_;
|
|
// a set of already seen rows
|
|
std::unordered_set<
|
|
std::vector<TypedValue>,
|
|
// use FNV collection hashing specialized for a vector of TypedValues
|
|
utils::FnvCollection<std::vector<TypedValue>, TypedValue,
|
|
TypedValue::Hash>,
|
|
TypedValueVectorEqual>
|
|
seen_rows_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class create-index (logical-operator)
|
|
((label "storage::Label" :reader t)
|
|
(property "storage::Property" :reader t))
|
|
(:documentation
|
|
"Creates an index for a combination of label and a property.
|
|
|
|
This operator takes no input and it shouldn't serve as an input to any
|
|
operator. Pulling from the cursor of this operator will create an index in
|
|
the database for the vertices which have the given label and property. In
|
|
case the index already exists, nothing happens.")
|
|
(:public
|
|
#>cpp
|
|
CreateIndex(storage::Label label, storage::Property property);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private #>cpp CreateIndex() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class union (logical-operator)
|
|
((left-op "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(right-op "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(union-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
|
|
(left-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
|
|
(right-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"A logical operator that applies UNION operator on inputs and places the
|
|
result on the frame.
|
|
|
|
This operator takes two inputs, a vector of symbols for the result, and
|
|
vectors of symbols used by each of the inputs.")
|
|
(:public
|
|
#>cpp
|
|
Union(const std::shared_ptr<LogicalOperator> &left_op,
|
|
const std::shared_ptr<LogicalOperator> &right_op,
|
|
const std::vector<Symbol> &union_symbols,
|
|
const std::vector<Symbol> &left_symbols,
|
|
const std::vector<Symbol> &right_symbols);
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp
|
|
Union() {}
|
|
|
|
class UnionCursor : public Cursor {
|
|
public:
|
|
UnionCursor(const Union &self, database::GraphDbAccessor &db);
|
|
bool Pull(Frame &, Context &) override;
|
|
void Reset() override;
|
|
|
|
private:
|
|
const Union &self_;
|
|
const std::unique_ptr<Cursor> left_cursor_, right_cursor_;
|
|
};
|
|
cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
;; TODO: We should probably output this operator in regular planner, not just
|
|
;; distributed planner.
|
|
(lcp:define-class cartesian (logical-operator)
|
|
((left-op "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(left-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
|
|
(right-op "std::shared_ptr<LogicalOperator>" :reader t
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(right-symbols "std::vector<Symbol>" :reader t
|
|
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
|
|
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
|
|
(:documentation
|
|
"Operator for producing a Cartesian product from 2 input branches")
|
|
(:public
|
|
#>cpp
|
|
/** Construct the operator with left input branch and right input branch. */
|
|
Cartesian(const std::shared_ptr<LogicalOperator> &left_op,
|
|
const std::vector<Symbol> &left_symbols,
|
|
const std::shared_ptr<LogicalOperator> &right_op,
|
|
const std::vector<Symbol> &right_symbols)
|
|
: left_op_(left_op),
|
|
left_symbols_(left_symbols),
|
|
right_op_(right_op),
|
|
right_symbols_(right_symbols) {}
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private #>cpp Cartesian() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
;;; KAFKA STREAM OPERATORS
|
|
(lcp:define-class create-stream (logical-operator)
|
|
((stream-name "std::string" :reader t)
|
|
(stream-uri "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(stream-topic "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(transform-uri "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(batch-interval-in-ms "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(batch-size "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Creates a new stream with given parameters that imports data.")
|
|
(:public
|
|
#>cpp
|
|
CreateStream(std::string stream_name, Expression *stream_uri,
|
|
Expression *stream_topic, Expression *transform_uri,
|
|
Expression *batch_interval_in_ms, Expression *batch_size);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp CreateStream() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class drop-stream (logical-operator)
|
|
((stream-name "std::string" :reader t))
|
|
(:documentation
|
|
"Stops and deletes a stream if it exists.")
|
|
(:public
|
|
#>cpp
|
|
explicit DropStream(std::string stream_name);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp DropStream() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class show-streams (logical-operator)
|
|
((name-symbol "Symbol" :reader t)
|
|
(uri-symbol "Symbol" :reader t)
|
|
(topic-symbol "Symbol" :reader t)
|
|
(transform-symbol "Symbol" :reader t)
|
|
(status-symbol "Symbol" :reader t))
|
|
(:documentation
|
|
"Shows all streams, started and stopped, that were configured.")
|
|
(:public
|
|
#>cpp
|
|
ShowStreams(Symbol name_symbol, Symbol endpoint_symbol, Symbol topic_symbol,
|
|
Symbol transform_symbol, Symbol status_symbol);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp ShowStreams() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class start-stop-stream (logical-operator)
|
|
((stream-name "std::string" :reader t)
|
|
(is-start :bool :reader t)
|
|
(limit-batches "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *")))
|
|
(:documentation
|
|
"Starts or stops a stream.
|
|
When starting a stream import, it can be limited by number of batches
|
|
If a stream is already running, it wont limit the number of batches that this
|
|
stream is importing.")
|
|
(:public
|
|
#>cpp
|
|
StartStopStream(std::string stream_name, bool is_start,
|
|
Expression *limit_batches);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp StartStopStream() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class start-stop-all-streams (logical-operator)
|
|
((stream-name "std::string" :reader t)
|
|
(is-start :bool :reader t))
|
|
(:documentation
|
|
"Starts or stops all stream.")
|
|
(:public
|
|
#>cpp
|
|
explicit StartStopAllStreams(bool is_start);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp StartStopAllStreams() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:define-class test-stream (logical-operator)
|
|
((stream-name "std::string" :reader t)
|
|
(limit-batches "Expression *"
|
|
:reader t
|
|
:capnp-type "Ast.Tree"
|
|
:capnp-init nil
|
|
:capnp-save #'save-ast-pointer
|
|
:capnp-load (load-ast-pointer "Expression *"))
|
|
(query-symbol "Symbol" :reader t)
|
|
(params-symbol "Symbol" :reader t))
|
|
(:documentation
|
|
"Test a stream. This will start consuming messages but wont insert anything
|
|
in the db.")
|
|
(:public
|
|
#>cpp
|
|
TestStream(std::string stream_name, Expression *limit_batches,
|
|
Symbol query_symbol, Symbol params_symbol);
|
|
DEFVISITABLE(HierarchicalLogicalOperatorVisitor);
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor &db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
|
|
return {};
|
|
}
|
|
|
|
bool HasSingleInput() const override;
|
|
std::shared_ptr<LogicalOperator> input() const override;
|
|
void set_input(std::shared_ptr<LogicalOperator>) override;
|
|
cpp<#)
|
|
(:private
|
|
#>cpp TestStream() {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
;;; END KAFKA STREAM OPERATORS
|
|
|
|
(lcp:define-class explain (logical-operator)
|
|
((input "std::shared_ptr<LogicalOperator>"
|
|
:capnp-save #'save-operator-pointer
|
|
:capnp-load #'load-operator-pointer)
|
|
(output-symbol "Symbol" :reader t))
|
|
(:documentation "Pretty print a LogicalOperator plan")
|
|
(:public
|
|
#>cpp
|
|
Explain(const std::shared_ptr<LogicalOperator> &input,
|
|
const Symbol &output_symbol);
|
|
|
|
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
|
std::unique_ptr<Cursor> MakeCursor(
|
|
database::GraphDbAccessor & db) const override;
|
|
std::vector<Symbol> OutputSymbols(const SymbolTable &) const override;
|
|
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
|
|
|
bool HasSingleInput() const override { return true; }
|
|
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
|
void set_input(std::shared_ptr<LogicalOperator> input) override {
|
|
input_ = input;
|
|
}
|
|
cpp<#)
|
|
(:private
|
|
#>cpp Explain () {} cpp<#)
|
|
(:serialize :capnp))
|
|
|
|
(lcp:pop-namespace) ;; plan
|
|
(lcp:pop-namespace) ;; query
|