A lot of work on query_engine. Still in progress!
This commit is contained in:
parent
b1459a891b
commit
356e9444ec
@ -16,6 +16,12 @@ struct Accessor : public ValueExpr<Accessor>
|
|||||||
|
|
||||||
Identifier* entity;
|
Identifier* entity;
|
||||||
Identifier* prop;
|
Identifier* prop;
|
||||||
|
|
||||||
|
bool has_entity() const { return entity != nullptr; }
|
||||||
|
bool has_prop() const { return prop != nullptr; }
|
||||||
|
|
||||||
|
std::string entity_name() const { return entity->name; }
|
||||||
|
std::string entity_prop() const { return prop->name; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ struct Boolean;
|
|||||||
struct Float;
|
struct Float;
|
||||||
struct Integer;
|
struct Integer;
|
||||||
struct String;
|
struct String;
|
||||||
|
struct Long;
|
||||||
|
|
||||||
// operators
|
// operators
|
||||||
struct And;
|
struct And;
|
||||||
@ -36,7 +37,7 @@ struct Slash;
|
|||||||
struct Rem;
|
struct Rem;
|
||||||
|
|
||||||
struct RelationshipSpecs;
|
struct RelationshipSpecs;
|
||||||
struct RelationshipList;
|
struct RelationshipTypeList;
|
||||||
struct Relationship;
|
struct Relationship;
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
@ -74,18 +75,15 @@ struct WithQuery;
|
|||||||
struct InternalIdExpr;
|
struct InternalIdExpr;
|
||||||
|
|
||||||
struct AstVisitor
|
struct AstVisitor
|
||||||
: public Visitor<Accessor, Boolean, Float, Identifier, Alias, Integer,
|
: public Visitor<
|
||||||
String, Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus,
|
Accessor, Boolean, Float, Identifier, Alias, Integer, String,
|
||||||
Minus, Star, Slash, Rem, PropertyList, RelationshipList,
|
Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus, Minus, Star, Slash,
|
||||||
Relationship, Node, RelationshipSpecs, LabelList,
|
Rem, PropertyList, RelationshipTypeList, Relationship, Node,
|
||||||
ReturnList, Pattern, PatternExpr, PatternList, Match,
|
RelationshipSpecs, LabelList, ReturnList, Pattern, PatternExpr,
|
||||||
ReadQuery, Start, Where, WriteQuery, Create, Return,
|
PatternList, Match, ReadQuery, Start, Where, WriteQuery, Create,
|
||||||
Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey,
|
Return, Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey,
|
||||||
ReadWriteQuery, IdentifierList,
|
ReadWriteQuery, IdentifierList, WithList, WithClause, WithQuery, Long,
|
||||||
WithList, WithClause, WithQuery,
|
InternalIdExpr, SetValue, SetElement, SetList>
|
||||||
InternalIdExpr,
|
|
||||||
SetValue, SetElement, SetList>
|
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@ struct List : public AstNode<Derived>
|
|||||||
|
|
||||||
T* value;
|
T* value;
|
||||||
Derived* next;
|
Derived* next;
|
||||||
|
|
||||||
|
bool has_value() const { return value != nullptr; }
|
||||||
|
bool has_next() const { return next != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,11 @@ struct Node : public AstNode<Node>
|
|||||||
Identifier* idn;
|
Identifier* idn;
|
||||||
LabelList* labels;
|
LabelList* labels;
|
||||||
PropertyList* props;
|
PropertyList* props;
|
||||||
|
|
||||||
|
bool has_identifier() const
|
||||||
|
{
|
||||||
|
return idn != nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ast_node.hpp"
|
#include "ast_node.hpp"
|
||||||
#include "relationship.hpp"
|
|
||||||
#include "node.hpp"
|
#include "node.hpp"
|
||||||
|
#include "relationship.hpp"
|
||||||
|
|
||||||
namespace ast
|
namespace ast
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Pattern : public AstNode<Pattern>
|
struct Pattern : public AstNode<Pattern>
|
||||||
{
|
{
|
||||||
Pattern(Node* node, Relationship* relationship, Pattern* next)
|
Pattern(Node *node, Relationship *relationship, Pattern *next)
|
||||||
: node(node), relationship(relationship), next(next) {}
|
: node(node), relationship(relationship), next(next)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Node* node;
|
Node *node;
|
||||||
Relationship* relationship;
|
Relationship *relationship;
|
||||||
Pattern* next;
|
Pattern *next;
|
||||||
|
|
||||||
|
bool has_node() const { return node != nullptr; }
|
||||||
|
bool has_relationship() const { return relationship != nullptr; }
|
||||||
|
bool has_next() const { return next != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PatternList : public List<Pattern, PatternList>
|
struct PatternList : public List<Pattern, PatternList>
|
||||||
{
|
{
|
||||||
using List::List;
|
using List::List;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,11 @@ struct Property : public AstNode<Property>
|
|||||||
|
|
||||||
Identifier* idn;
|
Identifier* idn;
|
||||||
Expr* value;
|
Expr* value;
|
||||||
|
|
||||||
|
bool has_name() const { return idn != nullptr; }
|
||||||
|
bool has_value() const { return value != nullptr; }
|
||||||
|
|
||||||
|
std::string name() const { return idn->name; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PropertyList : public List<Property, PropertyList>
|
struct PropertyList : public List<Property, PropertyList>
|
||||||
|
@ -1,35 +1,56 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "list.hpp"
|
|
||||||
#include "identifier.hpp"
|
#include "identifier.hpp"
|
||||||
|
#include "list.hpp"
|
||||||
|
|
||||||
namespace ast
|
namespace ast
|
||||||
{
|
{
|
||||||
|
|
||||||
struct RelationshipList : public List<Identifier, RelationshipList>
|
struct RelationshipTypeList : public List<Identifier, RelationshipTypeList>
|
||||||
{
|
{
|
||||||
using List::List;
|
using List::List;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RelationshipSpecs : public AstNode<RelationshipSpecs>
|
struct RelationshipSpecs : public AstNode<RelationshipSpecs>
|
||||||
{
|
{
|
||||||
RelationshipSpecs(Identifier* idn, RelationshipList* types, PropertyList* props)
|
RelationshipSpecs(Identifier *idn, RelationshipTypeList *types,
|
||||||
: idn(idn), types(types), props(props) {}
|
PropertyList *props)
|
||||||
|
: idn(idn), types(types), props(props)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Identifier* idn;
|
Identifier *idn;
|
||||||
RelationshipList* types;
|
RelationshipTypeList *types;
|
||||||
PropertyList* props;
|
PropertyList *props;
|
||||||
|
|
||||||
|
bool has_identifier() const { return idn != nullptr; }
|
||||||
|
|
||||||
|
std::string name() const { return idn->name; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Relationship : public AstNode<Relationship>
|
struct Relationship : public AstNode<Relationship>
|
||||||
{
|
{
|
||||||
enum Direction { Left, Right, Both };
|
enum Direction
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Both
|
||||||
|
};
|
||||||
|
|
||||||
Relationship(RelationshipSpecs* specs, Direction direction)
|
Relationship(RelationshipSpecs *specs, Direction direction)
|
||||||
: specs(specs), direction(direction) {}
|
: specs(specs), direction(direction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
RelationshipSpecs* specs;
|
RelationshipSpecs *specs;
|
||||||
Direction direction;
|
Direction direction;
|
||||||
};
|
|
||||||
|
|
||||||
|
bool has_relationship_specs() const { return specs != nullptr; }
|
||||||
|
bool has_name() const
|
||||||
|
{
|
||||||
|
return has_relationship_specs() && specs->has_identifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name() const { return specs->name(); }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ struct SetValue : public AstNode<SetValue>
|
|||||||
: value(value) {}
|
: value(value) {}
|
||||||
|
|
||||||
Expr* value;
|
Expr* value;
|
||||||
|
|
||||||
|
bool has_value() const { return value != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SetElement : public AstNode<SetElement>
|
struct SetElement : public AstNode<SetElement>
|
||||||
@ -40,6 +42,9 @@ struct SetElement : public AstNode<SetElement>
|
|||||||
|
|
||||||
Accessor* accessor;
|
Accessor* accessor;
|
||||||
SetValue* set_value;
|
SetValue* set_value;
|
||||||
|
|
||||||
|
bool has_accessor() const { return accessor != nullptr; }
|
||||||
|
bool has_value() const { return set_value != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SetList : public List<SetElement, SetList>
|
struct SetList : public List<SetElement, SetList>
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include "ast_node.hpp"
|
#include "ast_node.hpp"
|
||||||
#include "queries.hpp"
|
#include "queries.hpp"
|
||||||
|
|
||||||
// LEGACY TODO remove from here
|
|
||||||
|
|
||||||
namespace ast
|
namespace ast
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -39,15 +39,23 @@ struct String : public LeafExpr<std::string, String>
|
|||||||
|
|
||||||
struct InternalIdExpr : public Expr
|
struct InternalIdExpr : public Expr
|
||||||
{
|
{
|
||||||
InternalIdExpr(Identifier *identifier, Integer *value)
|
InternalIdExpr(Identifier *entity, Long *id)
|
||||||
: identifier(identifier), value(value)
|
: entity(entity), id(id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Identifier *identifier;
|
Identifier *entity;
|
||||||
Integer *value;
|
Long *id;
|
||||||
|
|
||||||
virtual void accept(AstVisitor &visitor) { visitor.visit(*this); }
|
virtual void accept(AstVisitor &visitor) { visitor.visit(*this); }
|
||||||
|
|
||||||
|
bool has_entity() const { return entity != nullptr; }
|
||||||
|
bool has_id() const { return id != nullptr; }
|
||||||
|
|
||||||
|
std::string entity_name() const { return entity->name; }
|
||||||
|
int64_t entity_id() const { return id->value; }
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ rel_idn(R) ::= . {
|
|||||||
R = nullptr;
|
R = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type rel_type {ast::RelationshipList*}
|
%type rel_type {ast::RelationshipTypeList*}
|
||||||
|
|
||||||
rel_type(L) ::= COLON rel_list(R). {
|
rel_type(L) ::= COLON rel_list(R). {
|
||||||
L = R;
|
L = R;
|
||||||
@ -274,14 +274,14 @@ rel_type(L) ::= . {
|
|||||||
L = nullptr;
|
L = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type rel_list {ast::RelationshipList*}
|
%type rel_list {ast::RelationshipTypeList*}
|
||||||
|
|
||||||
rel_list(L) ::= idn(I) PIPE rel_list(R). {
|
rel_list(L) ::= idn(I) PIPE rel_list(R). {
|
||||||
L = ast->create<ast::RelationshipList>(I, R);
|
L = ast->create<ast::RelationshipTypeList>(I, R);
|
||||||
}
|
}
|
||||||
|
|
||||||
rel_list(L) ::= idn(I). {
|
rel_list(L) ::= idn(I). {
|
||||||
L = ast->create<ast::RelationshipList>(I, nullptr);
|
L = ast->create<ast::RelationshipTypeList>(I, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
%type node {ast::Node*}
|
%type node {ast::Node*}
|
||||||
@ -444,9 +444,9 @@ value_expr(E) ::= idn(I) DOT idn(P). {
|
|||||||
E = ast->create<ast::Accessor>(I, P);
|
E = ast->create<ast::Accessor>(I, P);
|
||||||
}
|
}
|
||||||
|
|
||||||
value_expr(E) ::= ID LP idn(I) RP EQ INT(V). {
|
value_expr(E) ::= ID LP idn(I) RP EQ LONG(V). {
|
||||||
auto value = std::stoi(V->value);
|
auto value = std::stol(V->value);
|
||||||
E = ast->create<ast::InternalIdExpr>(I, ast->create<ast::Integer>(value));
|
E = ast->create<ast::InternalIdExpr>(I, ast->create<ast::Long>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
%type idn {ast::Identifier*}
|
%type idn {ast::Identifier*}
|
||||||
@ -469,9 +469,9 @@ identifier_list(L) ::= idn(I). {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
value_expr(E) ::= INT(V). {
|
value_expr(E) ::= LONG(V). {
|
||||||
auto value = std::stoi(V->value);
|
auto value = std::stol(V->value);
|
||||||
E = ast->create<ast::Integer>(value);
|
E = ast->create<ast::Long>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
value_expr(E) ::= FLOAT(V). {
|
value_expr(E) ::= FLOAT(V). {
|
||||||
|
@ -184,6 +184,12 @@ public:
|
|||||||
entry << "Integer " << integer.value;
|
entry << "Integer " << integer.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void visit(ast::Long& ast_long) override
|
||||||
|
{
|
||||||
|
auto entry = printer.advance();
|
||||||
|
entry << "Long " << ast_long.value;
|
||||||
|
}
|
||||||
|
|
||||||
// void visit(ast::ULong& ulong) override
|
// void visit(ast::ULong& ulong) override
|
||||||
// {
|
// {
|
||||||
// auto entry = printer.advance();
|
// auto entry = printer.advance();
|
||||||
@ -292,9 +298,9 @@ public:
|
|||||||
Traverser::visit(prop_list);
|
Traverser::visit(prop_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(ast::RelationshipList& rel_list) override
|
void visit(ast::RelationshipTypeList& rel_list) override
|
||||||
{
|
{
|
||||||
auto entry = printer.advance("Relationship List");
|
auto entry = printer.advance("Relationship Type List");
|
||||||
Traverser::visit(rel_list);
|
Traverser::visit(rel_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ public:
|
|||||||
rule("\\\"(.*?)\\\"", TK_STR);
|
rule("\\\"(.*?)\\\"", TK_STR);
|
||||||
|
|
||||||
// number
|
// number
|
||||||
rule("\\d+", TK_INT);
|
rule("\\d+", TK_LONG);
|
||||||
rule("\\d*[.]?\\d+", TK_FLOAT);
|
rule("\\d*[.]?\\d+", TK_FLOAT);
|
||||||
|
|
||||||
// identifier
|
// identifier
|
||||||
|
@ -166,7 +166,7 @@ public:
|
|||||||
accept(pattern_list.next);
|
accept(pattern_list.next);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(ast::RelationshipList& rel_list) override
|
void visit(ast::RelationshipTypeList& rel_list) override
|
||||||
{
|
{
|
||||||
accept(rel_list.value);
|
accept(rel_list.value);
|
||||||
accept(rel_list.next);
|
accept(rel_list.next);
|
||||||
@ -278,8 +278,8 @@ public:
|
|||||||
|
|
||||||
void visit(ast::InternalIdExpr& internal_id) override
|
void visit(ast::InternalIdExpr& internal_id) override
|
||||||
{
|
{
|
||||||
accept(internal_id.identifier);
|
accept(internal_id.entity);
|
||||||
accept(internal_id.value);
|
accept(internal_id.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "exceptions/query_engine_exception.hpp"
|
#include "exceptions/exceptions.hpp"
|
||||||
#include "utils/string/join.hpp"
|
#include "utils/string/join.hpp"
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
@ -32,11 +32,14 @@ public:
|
|||||||
|
|
||||||
// if compilation has failed throw exception
|
// if compilation has failed throw exception
|
||||||
if (compile_status == -1) {
|
if (compile_status == -1) {
|
||||||
throw QueryEngineException("Code compilation error");
|
throw QueryEngineException("Code compilation error. Generated code "
|
||||||
|
"is not compilable or compilation "
|
||||||
|
"settings are wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use logger
|
// TODO: use logger
|
||||||
std::cout << fmt::format("SUCCESS: Query Code Compilation: {} -> {}",
|
std::cout << fmt::format("SUCCESS: Query Code Compilation: {} -> {}",
|
||||||
in_file, out_file) << std::endl;
|
in_file, out_file)
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
#include "config/config.hpp"
|
#include "config/config.hpp"
|
||||||
#include "cypher/ast/ast.hpp"
|
#include "cypher/ast/ast.hpp"
|
||||||
#include "cypher/compiler.hpp"
|
#include "cypher/compiler.hpp"
|
||||||
|
#include "query_engine/exceptions/exceptions.hpp"
|
||||||
#include "template_engine/engine.hpp"
|
#include "template_engine/engine.hpp"
|
||||||
#include "traverser/code_traverser.hpp"
|
#include "traverser/cpp_traverser.hpp"
|
||||||
#include "utils/string/file.hpp"
|
#include "utils/string/file.hpp"
|
||||||
#include "query_engine/exceptions/query_engine_exception.hpp"
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// * logger
|
// * logger
|
||||||
@ -20,6 +20,11 @@ public:
|
|||||||
void generate_cpp(const std::string &query, const uint64_t stripped_hash,
|
void generate_cpp(const std::string &query, const uint64_t stripped_hash,
|
||||||
const std::string &path)
|
const std::string &path)
|
||||||
{
|
{
|
||||||
|
// TODO: optimize initialize only once -> be careful that object has
|
||||||
|
// a state
|
||||||
|
// TODO: multithread test
|
||||||
|
CppTraverser cpp_traverser;
|
||||||
|
|
||||||
// get paths
|
// get paths
|
||||||
string template_path = CONFIG(config::TEMPLATE_CPU_CPP_PATH);
|
string template_path = CONFIG(config::TEMPLATE_CPU_CPP_PATH);
|
||||||
string template_file = utils::read_file(template_path.c_str());
|
string template_file = utils::read_file(template_path.c_str());
|
||||||
@ -32,13 +37,14 @@ public:
|
|||||||
throw QueryEngineException("Syntax tree generation error");
|
throw QueryEngineException("Syntax tree generation error");
|
||||||
}
|
}
|
||||||
|
|
||||||
code_traverser.reset();
|
cpp_traverser.reset();
|
||||||
|
|
||||||
// code generation
|
// code generation
|
||||||
try {
|
try {
|
||||||
tree.root->accept(code_traverser);
|
tree.root->accept(cpp_traverser);
|
||||||
|
} catch (const SemanticException &e) {
|
||||||
|
throw e;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
// TODO: extract more information
|
|
||||||
throw QueryEngineException("Code generation error");
|
throw QueryEngineException("Code generation error");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +53,7 @@ public:
|
|||||||
template_file, {{"class_name", "CodeCPU"},
|
template_file, {{"class_name", "CodeCPU"},
|
||||||
{"stripped_hash", std::to_string(stripped_hash)},
|
{"stripped_hash", std::to_string(stripped_hash)},
|
||||||
{"query", query},
|
{"query", query},
|
||||||
{"code", code_traverser.code}});
|
{"code", cpp_traverser.code}});
|
||||||
|
|
||||||
// TODO: use logger, ifndef
|
// TODO: use logger, ifndef
|
||||||
std::cout << generated << std::endl;
|
std::cout << generated << std::endl;
|
||||||
@ -59,5 +65,4 @@ private:
|
|||||||
template_engine::TemplateEngine template_engine;
|
template_engine::TemplateEngine template_engine;
|
||||||
ast::Ast tree;
|
ast::Ast tree;
|
||||||
cypher::Compiler compiler;
|
cypher::Compiler compiler;
|
||||||
CodeTraverser code_traverser;
|
|
||||||
};
|
};
|
||||||
|
20
src/query_engine/code_generator/clause_action.hpp
Normal file
20
src/query_engine/code_generator/clause_action.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class ClauseAction : uint32_t
|
||||||
|
{
|
||||||
|
Undefined,
|
||||||
|
CreateNode,
|
||||||
|
MatchNode,
|
||||||
|
UpdateNode,
|
||||||
|
DeleteNode,
|
||||||
|
CreateRelationship,
|
||||||
|
MatchRelationship,
|
||||||
|
UpdateRelationship,
|
||||||
|
DeleteRelationship,
|
||||||
|
ReturnNode,
|
||||||
|
ReturnRelationship,
|
||||||
|
ReturnPack,
|
||||||
|
ReturnProjection
|
||||||
|
};
|
82
src/query_engine/code_generator/cpp_generator.hpp
Normal file
82
src/query_engine/code_generator/cpp_generator.hpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "query_engine/code_generator/cypher_state.hpp"
|
||||||
|
#include "query_engine/code_generator/query_action.hpp"
|
||||||
|
#include "query_engine/exceptions/exceptions.hpp"
|
||||||
|
|
||||||
|
class CppGenerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// !! multithread problem
|
||||||
|
// two threads shouldn't use this implementation at the same time
|
||||||
|
// !! TODO: REFACTOR
|
||||||
|
|
||||||
|
CppGenerator() : unprocessed_index(0), processed_index(0) { setup(); }
|
||||||
|
|
||||||
|
void state(CypherState state) { _cypher_state = state; }
|
||||||
|
|
||||||
|
CypherState state() { return _cypher_state; }
|
||||||
|
|
||||||
|
std::string generate()
|
||||||
|
{
|
||||||
|
std::string code = "";
|
||||||
|
|
||||||
|
for (uint64_t i = processed_index; i < unprocessed_index; ++i) {
|
||||||
|
auto &action = actions.at(i);
|
||||||
|
auto query_action = action.first;
|
||||||
|
if (action_functions.find(query_action) == action_functions.end())
|
||||||
|
throw CppGeneratorException(
|
||||||
|
"Query Action Function is not defined");
|
||||||
|
auto &action_data = action.second;
|
||||||
|
code += action_functions[query_action](_cypher_data, action_data);
|
||||||
|
++processed_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryActionData &add_action(const QueryAction &query_action)
|
||||||
|
{
|
||||||
|
unprocessed_index++;
|
||||||
|
actions.push_back(std::make_pair(query_action, QueryActionData()));
|
||||||
|
return action_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryActionData &action_data() { return actions.back().second; }
|
||||||
|
|
||||||
|
CypherStateData &cypher_data() { return _cypher_data; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
processed_index = 0;
|
||||||
|
unprocessed_index = 0;
|
||||||
|
actions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: setup function is going to be called every time
|
||||||
|
// when object of this class is constructed (optimize this)
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
action_functions[QueryAction::TransactionBegin] =
|
||||||
|
transaction_begin_action;
|
||||||
|
action_functions[QueryAction::Create] = create_query_action;
|
||||||
|
action_functions[QueryAction::Match] = match_query_action;
|
||||||
|
action_functions[QueryAction::Return] = return_query_action;
|
||||||
|
action_functions[QueryAction::Set] = set_query_action;
|
||||||
|
action_functions[QueryAction::TransactionCommit] =
|
||||||
|
transaction_commit_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t unprocessed_index;
|
||||||
|
uint64_t processed_index;
|
||||||
|
std::vector<std::pair<QueryAction, QueryActionData>> actions;
|
||||||
|
std::map<QueryAction,
|
||||||
|
std::function<std::string(CypherStateData &cypher_data,
|
||||||
|
QueryActionData &action_data)>>
|
||||||
|
action_functions;
|
||||||
|
CypherState _cypher_state;
|
||||||
|
CypherStateData _cypher_data;
|
||||||
|
};
|
86
src/query_engine/code_generator/cypher_state.hpp
Normal file
86
src/query_engine/code_generator/cypher_state.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// main states that are used while ast is traversed
|
||||||
|
// in order to generate ActionSequence
|
||||||
|
enum class CypherState : uint8_t
|
||||||
|
{
|
||||||
|
Undefined,
|
||||||
|
Match,
|
||||||
|
Where,
|
||||||
|
Create,
|
||||||
|
Set,
|
||||||
|
Return,
|
||||||
|
Delete
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EntityStatus : uint8_t
|
||||||
|
{
|
||||||
|
NotFound,
|
||||||
|
Matched,
|
||||||
|
Created
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EntityType : uint8_t
|
||||||
|
{
|
||||||
|
NotFound,
|
||||||
|
Node,
|
||||||
|
Relationship
|
||||||
|
};
|
||||||
|
|
||||||
|
class CypherStateData
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::map<std::string, EntityStatus> entity_status;
|
||||||
|
std::map<std::string, EntityType> entity_type;
|
||||||
|
// TODO: container that keeps track about c++ variable names
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool exist(const std::string& name) const
|
||||||
|
{
|
||||||
|
return entity_status.find(name) != entity_status.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityStatus status(const std::string &name)
|
||||||
|
{
|
||||||
|
if (entity_status.find(name) == entity_status.end())
|
||||||
|
return EntityStatus::NotFound;
|
||||||
|
|
||||||
|
return entity_status.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityType type(const std::string &name)
|
||||||
|
{
|
||||||
|
if (entity_type.find(name) == entity_type.end())
|
||||||
|
return EntityType::NotFound;
|
||||||
|
|
||||||
|
return entity_type.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_matched(const std::string &name)
|
||||||
|
{
|
||||||
|
entity_type[name] = EntityType::Node;
|
||||||
|
entity_status[name] = EntityStatus::Matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_created(const std::string &name)
|
||||||
|
{
|
||||||
|
entity_type[name] = EntityType::Node;
|
||||||
|
entity_status[name] = EntityStatus::Created;
|
||||||
|
}
|
||||||
|
|
||||||
|
void relationship_matched(const std::string &name)
|
||||||
|
{
|
||||||
|
entity_type[name] = EntityType::Relationship;
|
||||||
|
entity_status[name] = EntityStatus::Matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
void relationship_created(const std::string &name)
|
||||||
|
{
|
||||||
|
entity_type[name] = EntityType::Relationship;
|
||||||
|
entity_status[name] = EntityStatus::Created;
|
||||||
|
}
|
||||||
|
};
|
@ -116,6 +116,9 @@ public:
|
|||||||
entity_search::search_cost_t::SearchPlace
|
entity_search::search_cost_t::SearchPlace
|
||||||
min(const std::string &entity) const
|
min(const std::string &entity) const
|
||||||
{
|
{
|
||||||
|
if (_search_costs.find(entity) == _search_costs.end())
|
||||||
|
return entity_search::search_cost_t::SearchPlace::main_storage;
|
||||||
|
|
||||||
return _search_costs.at(entity).min();
|
return _search_costs.at(entity).min();
|
||||||
}
|
}
|
||||||
|
|
110
src/query_engine/code_generator/query_action.hpp
Normal file
110
src/query_engine/code_generator/query_action.hpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "query_engine/code_generator/cypher_state.hpp"
|
||||||
|
#include "query_engine/code_generator/query_action_data.hpp"
|
||||||
|
#include "query_engine/traverser/code.hpp"
|
||||||
|
|
||||||
|
using ParameterIndexKey::Type::InternalId;
|
||||||
|
|
||||||
|
// any entity (node or relationship) inside cypher query has an action
|
||||||
|
// that is associated with that entity
|
||||||
|
enum class QueryAction : uint32_t
|
||||||
|
{
|
||||||
|
TransactionBegin,
|
||||||
|
Create,
|
||||||
|
Match,
|
||||||
|
Set,
|
||||||
|
Return,
|
||||||
|
TransactionCommit
|
||||||
|
};
|
||||||
|
|
||||||
|
auto transaction_begin_action = [](CypherStateData &,
|
||||||
|
const QueryActionData &) -> std::string {
|
||||||
|
return LINE(code::transaction_begin);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_query_action =
|
||||||
|
[](CypherStateData &cypher_data,
|
||||||
|
const QueryActionData &action_data) -> std::string {
|
||||||
|
|
||||||
|
std::string code = "";
|
||||||
|
|
||||||
|
for (auto const &kv : action_data.actions) {
|
||||||
|
|
||||||
|
if (kv.second == ClauseAction::CreateRelationship) {
|
||||||
|
auto name = kv.first;
|
||||||
|
code += LINE(fmt::format(code::create_edge, name));
|
||||||
|
cypher_data.relationship_created(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto match_query_action =
|
||||||
|
[](CypherStateData &cypher_data,
|
||||||
|
const QueryActionData &action_data) -> std::string {
|
||||||
|
|
||||||
|
std::string code = "";
|
||||||
|
|
||||||
|
for (auto const &kv : action_data.actions) {
|
||||||
|
|
||||||
|
if (kv.second == ClauseAction::MatchNode) {
|
||||||
|
auto name = kv.first;
|
||||||
|
if (cypher_data.type(name) == EntityType::Node &&
|
||||||
|
cypher_data.status(name) == EntityStatus::Matched)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find node
|
||||||
|
if (kv.second == ClauseAction::MatchNode) {
|
||||||
|
auto name = kv.first;
|
||||||
|
auto place = action_data.csm.min(kv.first);
|
||||||
|
if (place == entity_search::search_internal_id) {
|
||||||
|
auto index = action_data.parameter_index.at(
|
||||||
|
ParameterIndexKey(InternalId, name));
|
||||||
|
code +=
|
||||||
|
LINE(fmt::format(code::match_vertex_by_id, name, index));
|
||||||
|
cypher_data.node_matched(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto set_query_action =
|
||||||
|
[](CypherStateData &cypher_data,
|
||||||
|
const QueryActionData &action_data) -> std::string {
|
||||||
|
|
||||||
|
std::string code = "";
|
||||||
|
|
||||||
|
for (auto const &kv : action_data.actions) {
|
||||||
|
auto name = kv.first;
|
||||||
|
if (kv.second == ClauseAction::UpdateNode &&
|
||||||
|
cypher_data.status(name) == EntityStatus::Matched &&
|
||||||
|
cypher_data.type(name) == EntityType::Node) {
|
||||||
|
auto entity_data = action_data.get_entity_property(name);
|
||||||
|
for (auto &property : entity_data.properties) {
|
||||||
|
auto index = action_data.parameter_index.at(ParameterIndexKey(name, property));
|
||||||
|
code += LINE(fmt::format(code::update_property, name, property, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto return_query_action = [](
|
||||||
|
CypherStateData &, const QueryActionData &) -> std::string { return ""; };
|
||||||
|
|
||||||
|
auto transaction_commit_action = [](CypherStateData &,
|
||||||
|
const QueryActionData &) -> std::string {
|
||||||
|
return LINE(code::transaction_commit) + LINE(code::return_empty_result);
|
||||||
|
};
|
144
src/query_engine/code_generator/query_action_data.hpp
Normal file
144
src/query_engine/code_generator/query_action_data.hpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "query_engine/exceptions/exceptions.hpp"
|
||||||
|
#include "query_engine/code_generator/clause_action.hpp"
|
||||||
|
#include "query_engine/code_generator/entity_search.hpp"
|
||||||
|
#include "storage/model/properties/all.hpp"
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
#include "utils/underlying_cast.hpp"
|
||||||
|
|
||||||
|
// used for storing data related to an entity (node or relationship)
|
||||||
|
// data can be:
|
||||||
|
// * tags: labels or type
|
||||||
|
// * props: property name, property value
|
||||||
|
struct EntityData
|
||||||
|
{
|
||||||
|
std::vector<std::string> tags;
|
||||||
|
std::vector<std::string> properties;
|
||||||
|
|
||||||
|
void add_tag(const std::string &tag) { tags.push_back(tag); }
|
||||||
|
void add_property(const std::string &property)
|
||||||
|
{
|
||||||
|
properties.push_back(property);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// used for storing indices of parameters (parameters are stripped before
|
||||||
|
// compiling process into the array), so somehow the compiler has to know
|
||||||
|
// how to find appropriate parameter during the compile process
|
||||||
|
//
|
||||||
|
// parameter index key can be related to:
|
||||||
|
// * internal_id and entity_name, e.g.:
|
||||||
|
// ID(n)=35445 -> ID(n)=2 -> index[PropertyIndexKey(Type::InternalId, n)] =
|
||||||
|
// 2
|
||||||
|
// * entity_name and entity_property, e.g.:
|
||||||
|
// n.name = "test" -> n.name = 3 -> index[PropertyIndexKey(entity_name,
|
||||||
|
// entity_property)] = 3
|
||||||
|
struct ParameterIndexKey
|
||||||
|
{
|
||||||
|
enum class Type : uint8_t
|
||||||
|
{
|
||||||
|
InternalId,
|
||||||
|
Projection
|
||||||
|
};
|
||||||
|
|
||||||
|
ParameterIndexKey(Type type, const std::string &entity_name)
|
||||||
|
: type(type), entity_name(entity_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterIndexKey(const std::string &entity_name,
|
||||||
|
const std::string &entity_property)
|
||||||
|
: type(Type::Projection), entity_name(entity_name),
|
||||||
|
entity_property(entity_property)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type type;
|
||||||
|
const std::string entity_name;
|
||||||
|
const std::string entity_property;
|
||||||
|
|
||||||
|
bool operator<(const ParameterIndexKey &rhs) const
|
||||||
|
{
|
||||||
|
runtime_assert(type == rhs.type,
|
||||||
|
"ParameterIndexKey types should be the same");
|
||||||
|
|
||||||
|
if (type == Type::InternalId) return entity_name < rhs.entity_name;
|
||||||
|
|
||||||
|
if (entity_name == rhs.entity_name)
|
||||||
|
return entity_property < rhs.entity_property;
|
||||||
|
|
||||||
|
return entity_name < rhs.entity_name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RelationshipData : public EntityData
|
||||||
|
{
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
};
|
||||||
|
|
||||||
|
using nodes_t = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
|
RelationshipData(nodes_t nodes, Direction direction)
|
||||||
|
: nodes(nodes), direction(direction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> nodes;
|
||||||
|
Direction direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QueryActionData
|
||||||
|
{
|
||||||
|
std::map<ParameterIndexKey, uint64_t> parameter_index;
|
||||||
|
std::map<std::string, ClauseAction> actions;
|
||||||
|
std::map<std::string, EntityData> entity_data;
|
||||||
|
std::map<std::string, RelationshipData> relationship_data;
|
||||||
|
CypherStateMachine csm;
|
||||||
|
|
||||||
|
QueryActionData() = default;
|
||||||
|
QueryActionData(QueryActionData &&other) = default;
|
||||||
|
|
||||||
|
void create_entity(const std::string &entity)
|
||||||
|
{
|
||||||
|
if (entity_data.find(entity) == entity_data.end())
|
||||||
|
entity_data.emplace(entity, EntityData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_entity_tag(const std::string &entity, const std::string &tag)
|
||||||
|
{
|
||||||
|
create_entity(entity);
|
||||||
|
entity_data.at(entity).add_tag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_entitiy_property(const std::string &entity,
|
||||||
|
const std::string &property)
|
||||||
|
{
|
||||||
|
create_entity(entity);
|
||||||
|
entity_data.at(entity).add_property(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor name
|
||||||
|
auto get_entity_property(const std::string& entity) const
|
||||||
|
{
|
||||||
|
if (entity_data.find(entity) == entity_data.end())
|
||||||
|
throw CppGeneratorException("Entity doesn't exist");
|
||||||
|
|
||||||
|
return entity_data.at(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print() const
|
||||||
|
{
|
||||||
|
for (auto const &action : actions) {
|
||||||
|
std::cout << action.first << " " << underlying_cast(action.second)
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
19
src/query_engine/code_generator/structures.hpp
Normal file
19
src/query_engine/code_generator/structures.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Vector : public std::vector<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using pair = std::pair<T, T>;
|
||||||
|
|
||||||
|
pair last_two()
|
||||||
|
{
|
||||||
|
runtime_assert(this->size() > 1, "Array size shoud be bigger than 1");
|
||||||
|
|
||||||
|
return std::make_pair(*(this->end() - 2), *(this->end() - 1));
|
||||||
|
}
|
||||||
|
};
|
18
src/query_engine/exceptions/exceptions.hpp
Normal file
18
src/query_engine/exceptions/exceptions.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils/exceptions/basic_exception.hpp"
|
||||||
|
|
||||||
|
class QueryEngineException : public BasicException
|
||||||
|
{
|
||||||
|
using BasicException::BasicException;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SemanticException : public BasicException
|
||||||
|
{
|
||||||
|
using BasicException::BasicException;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CppGeneratorException : public BasicException
|
||||||
|
{
|
||||||
|
using BasicException::BasicException;
|
||||||
|
};
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils/exceptions/basic_exception.hpp"
|
|
||||||
|
|
||||||
class QueryEngineException : public BasicException
|
|
||||||
{
|
|
||||||
using BasicException::BasicException;
|
|
||||||
};
|
|
@ -68,38 +68,39 @@ auto load_queries(Db& db)
|
|||||||
auto v2 = db.graph.vertices.find(t, args[1]->as<Int32>().value);
|
auto v2 = db.graph.vertices.find(t, args[1]->as<Int32>().value);
|
||||||
if (!v2) return t.commit(), false;
|
if (!v2) return t.commit(), false;
|
||||||
|
|
||||||
auto e = db.graph.edges.insert(t);
|
auto edge_accessor = db.graph.edges.insert(t);
|
||||||
|
|
||||||
v1.vlist->update(t)->data.out.add(e.vlist);
|
v1.vlist->update(t)->data.out.add(edge_accessor.vlist);
|
||||||
v2.vlist->update(t)->data.in.add(e.vlist);
|
v2.vlist->update(t)->data.in.add(edge_accessor.vlist);
|
||||||
|
|
||||||
e.from(v1.vlist);
|
edge_accessor.from(v1.vlist);
|
||||||
e.to(v2.vlist);
|
edge_accessor.to(v2.vlist);
|
||||||
|
|
||||||
auto &edge_type = db.graph.edge_type_store.find_or_create("IS");
|
auto &edge_type = db.graph.edge_type_store.find_or_create("IS");
|
||||||
e.edge_type(edge_type);
|
edge_accessor.edge_type(edge_type);
|
||||||
|
|
||||||
t.commit();
|
t.commit();
|
||||||
|
|
||||||
cout << e.edge_type() << endl;
|
cout << edge_accessor.edge_type() << endl;
|
||||||
cout_properties(e.properties());
|
|
||||||
|
cout_properties(edge_accessor.properties());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto find_edge_by_internal_id = [&db](const properties_t &args) {
|
auto find_edge_by_internal_id = [&db](const properties_t &args) {
|
||||||
auto &t = db.tx_engine.begin();
|
auto &t = db.tx_engine.begin();
|
||||||
auto e = db.graph.edges.find(t, args[0]->as<Int32>().value);
|
auto edge_accessor = db.graph.edges.find(t, args[0]->as<Int32>().value);
|
||||||
if (!e) return t.commit(), false;
|
if (!edge_accessor) return t.commit(), false;
|
||||||
|
|
||||||
// print edge type and properties
|
// print edge type and properties
|
||||||
cout << "EDGE_TYPE: " << e.edge_type() << endl;
|
cout << "EDGE_TYPE: " << edge_accessor.edge_type() << endl;
|
||||||
|
|
||||||
auto from = e.from();
|
auto from = edge_accessor.from();
|
||||||
cout << "FROM:" << endl;
|
cout << "FROM:" << endl;
|
||||||
cout_properties(from->find(t)->data.props);
|
cout_properties(from->find(t)->data.props);
|
||||||
|
|
||||||
auto to = e.to();
|
auto to = edge_accessor.to();
|
||||||
cout << "TO:" << endl;
|
cout << "TO:" << endl;
|
||||||
cout_properties(to->find(t)->data.props);
|
cout_properties(to->find(t)->data.props);
|
||||||
|
|
||||||
@ -139,6 +140,7 @@ auto load_queries(Db& db)
|
|||||||
queries[10597108978382323595u] = create_account;
|
queries[10597108978382323595u] = create_account;
|
||||||
queries[5397556489557792025u] = create_labeled_and_named_node;
|
queries[5397556489557792025u] = create_labeled_and_named_node;
|
||||||
queries[7939106225150551899u] = create_edge;
|
queries[7939106225150551899u] = create_edge;
|
||||||
|
queries[6579425155585886196u] = create_edge;
|
||||||
queries[11198568396549106428u] = find_node_by_internal_id;
|
queries[11198568396549106428u] = find_node_by_internal_id;
|
||||||
queries[8320600413058284114u] = find_edge_by_internal_id;
|
queries[8320600413058284114u] = find_edge_by_internal_id;
|
||||||
queries[6813335159006269041u] = update_node;
|
queries[6813335159006269041u] = update_node;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "query_engine/util.hpp"
|
#include "query_engine/util.hpp"
|
||||||
#include "query_program.hpp"
|
#include "query_program.hpp"
|
||||||
#include "utils/log/logger.hpp"
|
#include "utils/log/logger.hpp"
|
||||||
#include "query_engine/exceptions/query_engine_exception.hpp"
|
#include "query_engine/exceptions/exceptions.hpp"
|
||||||
|
|
||||||
// preparations before execution
|
// preparations before execution
|
||||||
// execution
|
// execution
|
||||||
|
@ -25,7 +25,7 @@ public:
|
|||||||
using sptr_code_lib = std::shared_ptr<CodeLib>;
|
using sptr_code_lib = std::shared_ptr<CodeLib>;
|
||||||
|
|
||||||
ProgramLoader()
|
ProgramLoader()
|
||||||
: stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL))
|
: stripper(make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,9 @@ public:
|
|||||||
// TODO load output path from config
|
// TODO load output path from config
|
||||||
auto base_path = config::Config::instance()[config::COMPILE_CPU_PATH];
|
auto base_path = config::Config::instance()[config::COMPILE_CPU_PATH];
|
||||||
auto path_cpp = base_path + hash_string + ".cpp";
|
auto path_cpp = base_path + hash_string + ".cpp";
|
||||||
code_generator.generate_cpp(query, stripped.hash, path_cpp);
|
auto stripped_space = stripper.strip_space(query);
|
||||||
|
code_generator.generate_cpp(stripped_space.query, stripped.hash,
|
||||||
|
path_cpp);
|
||||||
|
|
||||||
auto path_so = base_path + hash_string + ".so";
|
auto path_so = base_path + hash_string + ".so";
|
||||||
code_compiler.compile(path_cpp, path_so);
|
code_compiler.compile(path_cpp, path_so);
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
// query -> code_loader -> query_stripper -> [code_generator]
|
// query -> code_loader -> query_stripper -> [code_generator]
|
||||||
// -> [code_compiler] -> code_executor
|
// -> [code_compiler] -> code_executor
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// * query engine will get a pointer to currently active database
|
||||||
|
// * TCP server session will have pointer to dbms and currently active
|
||||||
|
// * database
|
||||||
|
|
||||||
class QueryEngine
|
class QueryEngine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -8,7 +8,7 @@ class QueryHasher
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QueryHasher()
|
QueryHasher()
|
||||||
: stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL)) {}
|
: stripper(make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL)) {}
|
||||||
|
|
||||||
std::string hash(std::string &query)
|
std::string hash(std::string &query)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ struct QueryStripped
|
|||||||
QueryStripped(QueryStripped &other) = delete;
|
QueryStripped(QueryStripped &other) = delete;
|
||||||
QueryStripped(QueryStripped &&other) = default;
|
QueryStripped(QueryStripped &&other) = default;
|
||||||
|
|
||||||
code_args_t arguments;
|
|
||||||
uint64_t hash;
|
|
||||||
std::string query;
|
std::string query;
|
||||||
|
uint64_t hash;
|
||||||
|
code_args_t arguments;
|
||||||
};
|
};
|
||||||
|
@ -8,13 +8,12 @@
|
|||||||
|
|
||||||
#include "cypher/cypher.h"
|
#include "cypher/cypher.h"
|
||||||
#include "cypher/tokenizer/cypher_lexer.hpp"
|
#include "cypher/tokenizer/cypher_lexer.hpp"
|
||||||
#include "utils/hashing/fnv.hpp"
|
|
||||||
#include "query_stripped.hpp"
|
#include "query_stripped.hpp"
|
||||||
#include "storage/model/properties/all.hpp"
|
#include "storage/model/properties/all.hpp"
|
||||||
|
#include "utils/hashing/fnv.hpp"
|
||||||
#include "utils/string/transform.hpp"
|
#include "utils/string/transform.hpp"
|
||||||
#include "utils/variadic/variadic.hpp"
|
#include "utils/variadic/variadic.hpp"
|
||||||
|
|
||||||
|
|
||||||
template <class T, class V>
|
template <class T, class V>
|
||||||
void store_query_param(code_args_t &arguments, V &&v)
|
void store_query_param(code_args_t &arguments, V &&v)
|
||||||
{
|
{
|
||||||
@ -39,7 +38,13 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
auto strip(const std::string &query)
|
auto strip_space(const std::string &query)
|
||||||
|
{
|
||||||
|
auto stripped = strip(query, " ");
|
||||||
|
return QueryStripped(std::move(stripped.query), stripped.hash, std::move(stripped.arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto strip(const std::string &query, const std::string &separator = "")
|
||||||
{
|
{
|
||||||
// TODO write this more optimal (resplace string
|
// TODO write this more optimal (resplace string
|
||||||
// concatenation with something smarter)
|
// concatenation with something smarter)
|
||||||
@ -60,9 +65,9 @@ public:
|
|||||||
if (_or(token.id, strip_types, std::make_index_sequence<size>{})) {
|
if (_or(token.id, strip_types, std::make_index_sequence<size>{})) {
|
||||||
auto index = counter++;
|
auto index = counter++;
|
||||||
switch (token.id) {
|
switch (token.id) {
|
||||||
case TK_INT:
|
case TK_LONG:
|
||||||
store_query_param<Int32>(stripped_arguments,
|
store_query_param<Int64>(stripped_arguments,
|
||||||
std::stoi(token.value));
|
std::stol(token.value));
|
||||||
break;
|
break;
|
||||||
case TK_STR:
|
case TK_STR:
|
||||||
store_query_param<String>(stripped_arguments, token.value);
|
store_query_param<String>(stripped_arguments, token.value);
|
||||||
@ -77,15 +82,14 @@ public:
|
|||||||
std::stof(token.value));
|
std::stof(token.value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stripped_query += std::to_string(index);
|
stripped_query += std::to_string(index) + separator;
|
||||||
} else {
|
} else {
|
||||||
// TODO: lowercase only keywords like (MATCH, CREATE, ...)
|
// TODO: lowercase only keywords like (MATCH, CREATE, ...)
|
||||||
stripped_query += token.value;
|
stripped_query += token.value + separator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QueryStripped(std::move(stripped_query),
|
return QueryStripped(std::move(stripped_query), fnv(stripped_query),
|
||||||
fnv(stripped_query),
|
|
||||||
std::move(stripped_arguments));
|
std::move(stripped_arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,6 @@
|
|||||||
#include "query_engine/i_code_cpu.hpp"
|
#include "query_engine/i_code_cpu.hpp"
|
||||||
#include "storage/model/properties/all.hpp"
|
#include "storage/model/properties/all.hpp"
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#include "query_engine/debug.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO generate with the template engine
|
|
||||||
// #include "storage/model/properties/jsonwriter.hpp"
|
|
||||||
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
|
||||||
|
@ -28,12 +28,22 @@ const std::string args_id = "auto id = args[{}]->as<Int32>();";
|
|||||||
const std::string vertex_accessor_args_id =
|
const std::string vertex_accessor_args_id =
|
||||||
"auto vertex_accessor = db.graph.vertices.find(t, id.value);";
|
"auto vertex_accessor = db.graph.vertices.find(t, id.value);";
|
||||||
|
|
||||||
|
const std::string match_vertex_by_id =
|
||||||
|
"auto {0} = db.graph.vertices.find(t, args[{1}]->as<Int64>().value);\n"
|
||||||
|
"\tif (!{0}) return t.commit(), std::make_shared<QueryResult>();";
|
||||||
|
|
||||||
|
const std::string create_edge =
|
||||||
|
"auto {} = db.graph.edges.insert(t);";
|
||||||
|
|
||||||
const std::string vertex_set_property =
|
const std::string vertex_set_property =
|
||||||
"vertex_accessor.property(\"{}\", args[{}]);";
|
"vertex_accessor.property(\"{}\", args[{}]);";
|
||||||
|
|
||||||
const std::string return_empty_result =
|
const std::string return_empty_result =
|
||||||
"return std::make_shared<QueryResult>();";
|
"return std::make_shared<QueryResult>();";
|
||||||
|
|
||||||
|
const std::string update_property =
|
||||||
|
"{}.property(\"{}\", args[{}]);";
|
||||||
|
|
||||||
const std::string create_label =
|
const std::string create_label =
|
||||||
"auto &{} = db.graph.label_store.find_or_create(\"{}\");";
|
"auto &{} = db.graph.label_store.find_or_create(\"{}\");";
|
||||||
|
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "cypher/visitor/traverser.hpp"
|
|
||||||
#include "cypher/ast/queries.hpp"
|
|
||||||
#include "code.hpp"
|
|
||||||
#include "write_traverser.hpp"
|
|
||||||
#include "read_traverser.hpp"
|
|
||||||
#include "update_traverser.hpp"
|
|
||||||
#include "delete_traverser.hpp"
|
|
||||||
#include "read_write_traverser.hpp"
|
|
||||||
|
|
||||||
class CodeTraverser : public Traverser, public Code
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
// TODO: remove code duplication
|
|
||||||
|
|
||||||
void visit(ast::WriteQuery& write_query) override
|
|
||||||
{
|
|
||||||
auto write_traverser = WriteTraverser();
|
|
||||||
write_query.accept(write_traverser);
|
|
||||||
code = write_traverser.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::ReadQuery& read_query) override
|
|
||||||
{
|
|
||||||
auto read_traverser = ReadTraverser();
|
|
||||||
read_query.accept(read_traverser);
|
|
||||||
code = read_traverser.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::UpdateQuery& update_query) override
|
|
||||||
{
|
|
||||||
auto update_traverser = UpdateTraverser();
|
|
||||||
update_query.accept(update_traverser);
|
|
||||||
code = update_traverser.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::DeleteQuery& delete_query) override
|
|
||||||
{
|
|
||||||
auto delete_traverser = DeleteTraverser();
|
|
||||||
delete_query.accept(delete_traverser);
|
|
||||||
code = delete_traverser.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::ReadWriteQuery& read_write_query) override
|
|
||||||
{
|
|
||||||
auto read_write_traverser = ReadWriteTraverser();
|
|
||||||
read_write_query.accept(read_write_traverser);
|
|
||||||
code = read_write_traverser.code;
|
|
||||||
}
|
|
||||||
};
|
|
410
src/query_engine/traverser/cpp_traverser.hpp
Normal file
410
src/query_engine/traverser/cpp_traverser.hpp
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "cypher/visitor/traverser.hpp"
|
||||||
|
#include "query_engine/code_generator/cpp_generator.hpp"
|
||||||
|
#include "query_engine/code_generator/entity_search.hpp"
|
||||||
|
#include "query_engine/code_generator/structures.hpp"
|
||||||
|
#include "query_engine/exceptions/exceptions.hpp"
|
||||||
|
#include "query_engine/traverser/code.hpp"
|
||||||
|
|
||||||
|
struct SetElementState
|
||||||
|
{
|
||||||
|
std::string set_entity;
|
||||||
|
std::string set_prop;
|
||||||
|
int64_t set_index;
|
||||||
|
|
||||||
|
SetElementState() { clear(); }
|
||||||
|
|
||||||
|
bool is_defined() const
|
||||||
|
{
|
||||||
|
if (set_entity.empty()) return false;
|
||||||
|
if (set_prop.empty()) return false;
|
||||||
|
if (set_index < 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
set_entity = "";
|
||||||
|
set_prop = "";
|
||||||
|
set_index = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropertyState
|
||||||
|
{
|
||||||
|
std::string property_name;
|
||||||
|
int64_t property_index;
|
||||||
|
|
||||||
|
PropertyState() { clear(); }
|
||||||
|
|
||||||
|
bool is_defined() const
|
||||||
|
{
|
||||||
|
if (property_name.empty()) return false;
|
||||||
|
if (property_index < 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
property_name = "";
|
||||||
|
property_index = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: another idea is to user ast history in order to choose appropriate
|
||||||
|
// action
|
||||||
|
|
||||||
|
class CppTraverser : public Traverser, public Code
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// currently active entity name (node or relationship name)
|
||||||
|
std::string entity;
|
||||||
|
|
||||||
|
// currently active state
|
||||||
|
CypherState state; // TODO: move to generator
|
||||||
|
QueryAction query_action;
|
||||||
|
ClauseAction clause_action;
|
||||||
|
|
||||||
|
// linearization
|
||||||
|
CppGenerator generator;
|
||||||
|
|
||||||
|
Vector<std::string> visited_nodes;
|
||||||
|
|
||||||
|
RelationshipData::Direction direction;
|
||||||
|
|
||||||
|
SetElementState set_element_state;
|
||||||
|
PropertyState property_state;
|
||||||
|
|
||||||
|
void clear_state()
|
||||||
|
{
|
||||||
|
set_element_state.clear();
|
||||||
|
property_state.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void visit(ast::WriteQuery &write_query) override
|
||||||
|
{
|
||||||
|
// TODO: crate top level node (TransactionBegin can be
|
||||||
|
// only at the one place)
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
|
||||||
|
Traverser::visit(write_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::ReadQuery &read_query) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
|
||||||
|
Traverser::visit(read_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::UpdateQuery &update_query) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
|
||||||
|
Traverser::visit(update_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::DeleteQuery &delete_query) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
|
||||||
|
Traverser::visit(delete_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::ReadWriteQuery &read_write_query) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
|
||||||
|
Traverser::visit(read_write_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Match &ast_match) override
|
||||||
|
{
|
||||||
|
state = CypherState::Match;
|
||||||
|
|
||||||
|
generator.add_action(QueryAction::Match);
|
||||||
|
|
||||||
|
Traverser::visit(ast_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Where &ast_where) override
|
||||||
|
{
|
||||||
|
state = CypherState::Where;
|
||||||
|
|
||||||
|
Traverser::visit(ast_where);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Create &ast_create) override
|
||||||
|
{
|
||||||
|
code += generator.generate();
|
||||||
|
generator.add_action(QueryAction::Create);
|
||||||
|
|
||||||
|
state = CypherState::Create;
|
||||||
|
query_action = QueryAction::Create;
|
||||||
|
|
||||||
|
Traverser::visit(ast_create);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Set &ast_set) override
|
||||||
|
{
|
||||||
|
code += generator.generate();
|
||||||
|
|
||||||
|
generator.add_action(QueryAction::Set);
|
||||||
|
|
||||||
|
state = CypherState::Set;
|
||||||
|
query_action = QueryAction::Set;
|
||||||
|
|
||||||
|
Traverser::visit(ast_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Return &ast_return) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::Return);
|
||||||
|
|
||||||
|
state = CypherState::Return;
|
||||||
|
query_action = QueryAction::Return;
|
||||||
|
|
||||||
|
Traverser::visit(ast_return);
|
||||||
|
|
||||||
|
// TODO: move commit somewhare that is more appropriate
|
||||||
|
generator.add_action(QueryAction::TransactionCommit);
|
||||||
|
|
||||||
|
code += generator.generate();
|
||||||
|
|
||||||
|
generator.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::PatternList &ast_pattern_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_pattern_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Pattern &ast_pattern) override
|
||||||
|
{
|
||||||
|
// TODO: Is that traversal order OK for all cases? Probably NOT.
|
||||||
|
if (ast_pattern.has_next()) {
|
||||||
|
Traverser::visit(*(ast_pattern.next));
|
||||||
|
if (ast_pattern.has_node()) Traverser::visit(*(ast_pattern.node));
|
||||||
|
if (ast_pattern.has_relationship())
|
||||||
|
Traverser::visit(*(ast_pattern.relationship));
|
||||||
|
} else {
|
||||||
|
Traverser::visit(ast_pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Node &ast_node) override
|
||||||
|
{
|
||||||
|
auto &action_data = generator.action_data();
|
||||||
|
auto &cypher_data = generator.cypher_data();
|
||||||
|
|
||||||
|
if (!ast_node.has_identifier()) return;
|
||||||
|
|
||||||
|
auto name = ast_node.idn->name;
|
||||||
|
entity = name;
|
||||||
|
visited_nodes.push_back(name);
|
||||||
|
|
||||||
|
if (state == CypherState::Match) {
|
||||||
|
action_data.actions[name] = ClauseAction::MatchNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == CypherState::Create) {
|
||||||
|
if (cypher_data.status(name) == EntityStatus::Matched) {
|
||||||
|
action_data.actions[name] = ClauseAction::MatchNode;
|
||||||
|
} else {
|
||||||
|
action_data.actions[name] = ClauseAction::CreateNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Traverser::visit(ast_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::And &ast_and) override { Traverser::visit(ast_and); }
|
||||||
|
|
||||||
|
void visit(ast::InternalIdExpr &internal_id_expr) override
|
||||||
|
{
|
||||||
|
if (!internal_id_expr.has_entity()) return;
|
||||||
|
if (!internal_id_expr.has_id()) return;
|
||||||
|
|
||||||
|
auto name = internal_id_expr.entity_name();
|
||||||
|
// because entity_id will be index of value inside parameters array
|
||||||
|
auto index = internal_id_expr.entity_id();
|
||||||
|
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
data.parameter_index[ParameterIndexKey(InternalId, name)] = index;
|
||||||
|
data.csm.search_cost(name, entity_search::search_internal_id,
|
||||||
|
entity_search::internal_id_cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- RELATIONSHIP --
|
||||||
|
void create_relationship(const std::string &name)
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
data.actions[name] = ClauseAction::CreateRelationship;
|
||||||
|
auto nodes = visited_nodes.last_two();
|
||||||
|
data.relationship_data.emplace(name,
|
||||||
|
RelationshipData(nodes, direction));
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Relationship &ast_relationship) override
|
||||||
|
{
|
||||||
|
auto &cypher_data = generator.cypher_data();
|
||||||
|
|
||||||
|
if (ast_relationship.has_name()) entity = ast_relationship.name();
|
||||||
|
|
||||||
|
// TODO: simplify somehow
|
||||||
|
if (state == CypherState::Create) {
|
||||||
|
if (ast_relationship.has_name()) {
|
||||||
|
auto name = ast_relationship.name();
|
||||||
|
if (!cypher_data.exist(name)) {
|
||||||
|
clause_action = ClauseAction::CreateRelationship;
|
||||||
|
create_relationship(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using ast_direction = ast::Relationship::Direction;
|
||||||
|
using generator_direction = RelationshipData::Direction;
|
||||||
|
|
||||||
|
if (ast_relationship.direction == ast_direction::Left)
|
||||||
|
direction = generator_direction::Left;
|
||||||
|
|
||||||
|
if (ast_relationship.direction == ast_direction::Right)
|
||||||
|
direction = generator_direction::Right;
|
||||||
|
|
||||||
|
Traverser::visit(ast_relationship);
|
||||||
|
|
||||||
|
// TODO: add suport for Direction::Both
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::RelationshipSpecs &ast_relationship_specs) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_relationship_specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::RelationshipTypeList &ast_relationship_type_list) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (ast_relationship_type_list.has_value()) {
|
||||||
|
auto type = ast_relationship_type_list.value->name;
|
||||||
|
data.add_entity_tag(entity, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Traverser::visit(ast_relationship_type_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::LabelList &ast_label_list) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (!ast_label_list.has_value()) return;
|
||||||
|
|
||||||
|
auto label = ast_label_list.value->name;
|
||||||
|
|
||||||
|
data.add_entity_tag(entity, label);
|
||||||
|
|
||||||
|
Traverser::visit(ast_label_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::PropertyList &ast_property_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_property_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Property &ast_property) override
|
||||||
|
{
|
||||||
|
clear_state();
|
||||||
|
|
||||||
|
Traverser::visit(ast_property);
|
||||||
|
|
||||||
|
// TODO: too ugly refactor somehow (clear_state part is awful)
|
||||||
|
if (entity.empty() || !property_state.is_defined()) {
|
||||||
|
clear_state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prop = property_state.property_name;
|
||||||
|
auto index = property_state.property_index;
|
||||||
|
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
data.parameter_index.emplace(ParameterIndexKey(entity, prop), index);
|
||||||
|
data.add_entitiy_property(entity, prop);
|
||||||
|
|
||||||
|
clear_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Identifier &ast_identifier) override
|
||||||
|
{
|
||||||
|
property_state.property_name = ast_identifier.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Long &ast_long) override
|
||||||
|
{
|
||||||
|
set_element_state.set_index = ast_long.value;
|
||||||
|
property_state.property_index = ast_long.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- SET subtree (node)
|
||||||
|
// QUERY: SET n.name = "bla", ...
|
||||||
|
// STRIP: SET n.name = 0, ...
|
||||||
|
// STATE: entity: n; prop: name; set_index: 0
|
||||||
|
|
||||||
|
void visit(ast::SetList &ast_set_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_set_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::SetElement &ast_set_element) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_set_element);
|
||||||
|
|
||||||
|
if (!set_element_state.is_defined()) {
|
||||||
|
clear_state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entity = set_element_state.set_entity;
|
||||||
|
auto prop = set_element_state.set_prop;
|
||||||
|
auto index = set_element_state.set_index;
|
||||||
|
|
||||||
|
auto &cypher_data = generator.cypher_data();
|
||||||
|
auto entity_type = cypher_data.type(entity);
|
||||||
|
|
||||||
|
if (entity_type == EntityType::NotFound)
|
||||||
|
throw SemanticException("Set Entity (" + entity +
|
||||||
|
") doesn't exist");
|
||||||
|
|
||||||
|
auto &action_data = generator.action_data();
|
||||||
|
|
||||||
|
if (entity_type == EntityType::Node)
|
||||||
|
action_data.actions[entity] = ClauseAction::UpdateNode;
|
||||||
|
if (entity_type == EntityType::Relationship)
|
||||||
|
action_data.actions[entity] = ClauseAction::UpdateRelationship;
|
||||||
|
|
||||||
|
action_data.parameter_index.emplace(ParameterIndexKey(entity, prop), index);
|
||||||
|
action_data.add_entitiy_property(entity, prop);
|
||||||
|
|
||||||
|
clear_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Accessor &ast_accessor) override
|
||||||
|
{
|
||||||
|
if (!ast_accessor.has_entity()) return;
|
||||||
|
if (!ast_accessor.has_prop()) return;
|
||||||
|
|
||||||
|
set_element_state.set_entity = ast_accessor.entity_name();
|
||||||
|
set_element_state.set_prop = ast_accessor.entity_prop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::SetValue &ast_set_value) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_set_value);
|
||||||
|
}
|
||||||
|
};
|
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
#include "code.hpp"
|
#include "code.hpp"
|
||||||
#include "cypher/visitor/traverser.hpp"
|
#include "cypher/visitor/traverser.hpp"
|
||||||
#include "query_engine/state_machine/cypher.hpp"
|
|
||||||
#include "query_engine/util.hpp"
|
#include "query_engine/util.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
#include "utils/underlying_cast.hpp"
|
||||||
|
#include "query_engine/code_generator/entity_search.hpp"
|
||||||
|
|
||||||
using namespace entity_search;
|
using namespace entity_search;
|
||||||
|
|
270
src/query_engine/traverser/del_read_write_traverser.hpp
Normal file
270
src/query_engine/traverser/del_read_write_traverser.hpp
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "code.hpp"
|
||||||
|
#include "cypher/visitor/traverser.hpp"
|
||||||
|
#include "query_engine/code_generator/cypher_state.hpp"
|
||||||
|
#include "query_engine/code_generator/cpp_generator.hpp"
|
||||||
|
#include "query_engine/code_generator/structures.hpp"
|
||||||
|
|
||||||
|
// k
|
||||||
|
// auto v1 = db.graph.vertices.find(t, args[0]->as<Int32>().value);
|
||||||
|
// if (!v1) return t.commit(), false;
|
||||||
|
|
||||||
|
// auto v2 = db.graph.vertices.find(t, args[1]->as<Int32>().value);
|
||||||
|
// if (!v2) return t.commit(), false;
|
||||||
|
|
||||||
|
// create
|
||||||
|
// auto edge_accessor = db.graph.edges.insert(t);
|
||||||
|
|
||||||
|
// from
|
||||||
|
// v1.vlist->update(t)->data.out.add(edge_accessor.vlist);
|
||||||
|
// edge_accessor.from(v1.vlist);
|
||||||
|
|
||||||
|
// to
|
||||||
|
// v2.vlist->update(t)->data.in.add(edge_accessor.vlist);
|
||||||
|
// edge_accessor.to(v2.vlist);
|
||||||
|
|
||||||
|
// edge type
|
||||||
|
// auto &edge_type = db.graph.edge_type_store.find_or_create("IS");
|
||||||
|
// edge_accessor.edge_type(edge_type);
|
||||||
|
|
||||||
|
class ReadWriteTraverser : public Traverser, public Code
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// currently active entity name (node or relationship name)
|
||||||
|
std::string entity;
|
||||||
|
|
||||||
|
// currenlty active parameter index
|
||||||
|
uint64_t index;
|
||||||
|
|
||||||
|
// currently active state
|
||||||
|
CypherState state; // TODO: move to generator
|
||||||
|
QueryAction query_action;
|
||||||
|
ClauseAction clause_action;
|
||||||
|
|
||||||
|
// linearization
|
||||||
|
CppGenerator generator;
|
||||||
|
|
||||||
|
Vector<std::string> visited_nodes;
|
||||||
|
|
||||||
|
RelationshipData::Direction direction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void visit(ast::Match &ast_match) override
|
||||||
|
{
|
||||||
|
state = CypherState::Match;
|
||||||
|
|
||||||
|
generator.add_action(QueryAction::TransactionBegin);
|
||||||
|
generator.add_action(QueryAction::Match);
|
||||||
|
|
||||||
|
Traverser::visit(ast_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Where &ast_where) override
|
||||||
|
{
|
||||||
|
state = CypherState::Where;
|
||||||
|
|
||||||
|
Traverser::visit(ast_where);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Create &ast_create) override
|
||||||
|
{
|
||||||
|
// generate state before
|
||||||
|
generator.generate();
|
||||||
|
|
||||||
|
// save current action
|
||||||
|
generator.add_action(QueryAction::Create);
|
||||||
|
|
||||||
|
state = CypherState::Create;
|
||||||
|
query_action = QueryAction::Create;
|
||||||
|
|
||||||
|
Traverser::visit(ast_create);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Return &ast_return) override
|
||||||
|
{
|
||||||
|
generator.add_action(QueryAction::Return);
|
||||||
|
generator.add_action(QueryAction::TransactionCommit);
|
||||||
|
|
||||||
|
state = CypherState::Return;
|
||||||
|
query_action = QueryAction::Return;
|
||||||
|
|
||||||
|
Traverser::visit(ast_return);
|
||||||
|
|
||||||
|
code = generator.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::PatternList &ast_pattern_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_pattern_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Pattern &ast_pattern) override
|
||||||
|
{
|
||||||
|
// TODO: Is that traversal order OK for all cases? Probably NOT.
|
||||||
|
if (ast_pattern.has_next()) {
|
||||||
|
Traverser::visit(*(ast_pattern.next));
|
||||||
|
if (ast_pattern.has_node()) Traverser::visit(*(ast_pattern.node));
|
||||||
|
if (ast_pattern.has_relationship())
|
||||||
|
Traverser::visit(*(ast_pattern.relationship));
|
||||||
|
} else {
|
||||||
|
Traverser::visit(ast_pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Node &ast_node) override
|
||||||
|
{
|
||||||
|
auto &action_data = generator.action_data();
|
||||||
|
auto &cypher_data = generator.cypher_data();
|
||||||
|
|
||||||
|
if (!ast_node.has_identifier()) return;
|
||||||
|
|
||||||
|
auto name = ast_node.idn->name;
|
||||||
|
entity = name;
|
||||||
|
visited_nodes.push_back(name);
|
||||||
|
|
||||||
|
if (state == CypherState::Match) {
|
||||||
|
action_data.actions[name] = ClauseAction::MatchNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == CypherState::Create) {
|
||||||
|
if (cypher_data.status(name) == EntityStatus::Matched) {
|
||||||
|
action_data.actions[name] = ClauseAction::MatchNode;
|
||||||
|
} else {
|
||||||
|
action_data.actions[name] = ClauseAction::CreateNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Traverser::visit(ast_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::And &ast_and) override { Traverser::visit(ast_and); }
|
||||||
|
|
||||||
|
void visit(ast::InternalIdExpr &internal_id_expr) override
|
||||||
|
{
|
||||||
|
if (internal_id_expr.identifier == nullptr) return;
|
||||||
|
auto name = internal_id_expr.identifier->name;
|
||||||
|
|
||||||
|
// value has to exist
|
||||||
|
if (internal_id_expr.value == nullptr) {
|
||||||
|
// TODO: raise exception
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
data.parameter_index[ParameterIndexKey(InternalId, name)] = index++;
|
||||||
|
data.csm.search_cost(name, search_internal_id, internal_id_cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- RELATIONSHIP --
|
||||||
|
void create_relationship(const std::string &name)
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
data.actions[name] = ClauseAction::CreateRelationship;
|
||||||
|
auto nodes = visited_nodes.last_two();
|
||||||
|
data.relationship_data.emplace(name,
|
||||||
|
RelationshipData(nodes, direction));
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Relationship &ast_relationship) override
|
||||||
|
{
|
||||||
|
auto &cypher_data = generator.cypher_data();
|
||||||
|
|
||||||
|
if (ast_relationship.has_name()) entity = ast_relationship.name();
|
||||||
|
|
||||||
|
// TODO: simplify somehow
|
||||||
|
if (state == CypherState::Create) {
|
||||||
|
if (ast_relationship.has_name()) {
|
||||||
|
auto name = ast_relationship.name();
|
||||||
|
if (!cypher_data.exist(name)) {
|
||||||
|
clause_action = ClauseAction::CreateRelationship;
|
||||||
|
create_relationship(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using ast_direction = ast::Relationship::Direction;
|
||||||
|
using generator_direction = RelationshipData::Direction;
|
||||||
|
|
||||||
|
if (ast_relationship.direction == ast_direction::Left)
|
||||||
|
direction = generator_direction::Left;
|
||||||
|
|
||||||
|
if (ast_relationship.direction == ast_direction::Right)
|
||||||
|
direction = generator_direction::Right;
|
||||||
|
|
||||||
|
Traverser::visit(ast_relationship);
|
||||||
|
|
||||||
|
// TODO: add suport for Direction::Both
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::RelationshipSpecs &ast_relationship_specs) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_relationship_specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::RelationshipTypeList &ast_relationship_type_list) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (ast_relationship_type_list.has_value()) {
|
||||||
|
auto type = ast_relationship_type_list.value->name;
|
||||||
|
data.add_entity_tag(entity, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Traverser::visit(ast_relationship_type_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::LabelList &ast_label_list) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (!ast_label_list.has_value()) return;
|
||||||
|
|
||||||
|
auto label = ast_label_list.value->name;
|
||||||
|
|
||||||
|
data.add_entity_tag(entity, label);
|
||||||
|
|
||||||
|
Traverser::visit(ast_label_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::PropertyList& ast_property_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_property_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Property &ast_property) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (!ast_property.has_name()) return;
|
||||||
|
|
||||||
|
auto name = ast_property.name();
|
||||||
|
|
||||||
|
data.parameter_index.emplace(ParameterIndexKey(entity, name), index++);
|
||||||
|
|
||||||
|
Traverser::visit(ast_property);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- SET --
|
||||||
|
|
||||||
|
void visit(ast::SetList &ast_set_list) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_set_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::SetElement &ast_set_element) override
|
||||||
|
{
|
||||||
|
Traverser::visit(ast_set_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(ast::Accessor &ast_accessor) override
|
||||||
|
{
|
||||||
|
auto &data = generator.action_data();
|
||||||
|
|
||||||
|
if (ast_accessor.has_entity() && ast_accessor.has_prop()) {
|
||||||
|
auto entity = ast_accessor.entity_name();
|
||||||
|
auto prop = ast_accessor.entity_prop();
|
||||||
|
data.parameter_index.emplace(ParameterIndexKey(entity, prop),
|
||||||
|
index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "code.hpp"
|
|
||||||
#include "cypher/visitor/traverser.hpp"
|
|
||||||
|
|
||||||
class DeleteTraverser : public Traverser, public Code
|
|
||||||
{
|
|
||||||
void visit(ast::Match& match) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "MATCH"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Delete& delete_clause) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "DELETE"));
|
|
||||||
code += LINE(code::return_empty_result);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "code.hpp"
|
|
||||||
#include "cypher/visitor/traverser.hpp"
|
|
||||||
|
|
||||||
class ReadWriteTraverser : public Traverser, public Code
|
|
||||||
{
|
|
||||||
void visit(ast::Match& match) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "MATCH"));
|
|
||||||
Traverser::visit(match);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Where& where) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "WHERE"));
|
|
||||||
Traverser::visit(where);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Create& create) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "CREATE"));
|
|
||||||
Traverser::visit(create);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Return& ast_return) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "RETURN"));
|
|
||||||
code += LINE(code::return_empty_result);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "code.hpp"
|
|
||||||
#include "cypher/visitor/traverser.hpp"
|
|
||||||
|
|
||||||
class UpdateTraverser : public Traverser, public Code
|
|
||||||
{
|
|
||||||
void visit(ast::Match& match) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "MATCH"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Set& set) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "SET"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(ast::Return& ret) override
|
|
||||||
{
|
|
||||||
code += LINE(fmt::format(code::todo, "RETURN"));
|
|
||||||
code += LINE(code::return_empty_result);
|
|
||||||
}
|
|
||||||
};
|
|
@ -61,16 +61,31 @@ set_property(TARGET integration_queries PROPERTY CXX_STANDARD 14)
|
|||||||
|
|
||||||
## MANUAL TESTS
|
## MANUAL TESTS
|
||||||
|
|
||||||
# query engine
|
# cypher_ast
|
||||||
|
add_executable(manual_cypher_ast manual/cypher_ast.cpp ${memgraph_src_files})
|
||||||
|
target_link_libraries(manual_cypher_ast ${fmt_static_lib})
|
||||||
|
target_link_libraries(manual_cypher_ast cypher_lib)
|
||||||
|
set_property(TARGET manual_cypher_ast PROPERTY CXX_STANDARD 14)
|
||||||
|
|
||||||
|
# queries
|
||||||
|
add_executable(manual_queries manual/queries.cpp ${memgraph_src_files})
|
||||||
|
target_link_libraries(manual_queries ${fmt_static_lib})
|
||||||
|
target_link_libraries(manual_queries cypher_lib)
|
||||||
|
set_property(TARGET manual_queries PROPERTY CXX_STANDARD 14)
|
||||||
|
|
||||||
|
# query_engine
|
||||||
add_executable(manual_query_engine manual/query_engine.cpp ${memgraph_src_files})
|
add_executable(manual_query_engine manual/query_engine.cpp ${memgraph_src_files})
|
||||||
target_link_libraries(manual_query_engine ${fmt_static_lib})
|
target_link_libraries(manual_query_engine ${fmt_static_lib})
|
||||||
target_link_libraries(manual_query_engine dl)
|
target_link_libraries(manual_query_engine dl)
|
||||||
target_link_libraries(manual_query_engine cypher_lib)
|
target_link_libraries(manual_query_engine cypher_lib)
|
||||||
set_property(TARGET manual_query_engine PROPERTY CXX_STANDARD 14)
|
set_property(TARGET manual_query_engine PROPERTY CXX_STANDARD 14)
|
||||||
|
|
||||||
# ast traversal
|
# query_hasher
|
||||||
add_executable(cypher_ast_traverser manual/cypher_ast.cpp ${mamgraph_src_files})
|
add_executable(manual_query_hasher manual/query_hasher.cpp ${memgraph_src_files})
|
||||||
target_link_libraries(cypher_ast_traverser ${fmt_static_lib})
|
target_link_libraries(manual_query_hasher ${fmt_static_lib})
|
||||||
target_link_libraries(cypher_ast_traverser cypher_lib)
|
set_property(TARGET manual_query_hasher PROPERTY CXX_STANDARD 14)
|
||||||
set_property(TARGET cypher_ast_traverser PROPERTY CXX_STANDARD 14)
|
|
||||||
|
|
||||||
|
# query_action_processor
|
||||||
|
add_executable(manual_cpp_generator manual/cpp_generator.cpp ${memgraph_src_files})
|
||||||
|
target_link_libraries(manual_cpp_generator ${fmt_static_lib})
|
||||||
|
set_property(TARGET manual_cpp_generator PROPERTY CXX_STANDARD 14)
|
||||||
|
@ -7,7 +7,7 @@ int main(void)
|
|||||||
|
|
||||||
auto query_functions = load_queries(db);
|
auto query_functions = load_queries(db);
|
||||||
|
|
||||||
auto stripper = make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL);
|
auto stripper = make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL);
|
||||||
|
|
||||||
// TODO: put all queries into a file
|
// TODO: put all queries into a file
|
||||||
std::vector<std::string> queries = {
|
std::vector<std::string> queries = {
|
||||||
|
34
tests/manual/cpp_generator.cpp
Normal file
34
tests/manual/cpp_generator.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "query_engine/code_generator/cpp_generator.hpp"
|
||||||
|
|
||||||
|
using ParameterIndexKey::Type::InternalId;
|
||||||
|
using ParameterIndexKey::Type::Projection;
|
||||||
|
|
||||||
|
auto main() -> int
|
||||||
|
{
|
||||||
|
CppGenerator generator;
|
||||||
|
|
||||||
|
// MATCH
|
||||||
|
generator.add_action(QueryAction::Match);
|
||||||
|
|
||||||
|
auto& data_match = generator.action_data();
|
||||||
|
data_match.actions["n1"] = ClauseAction::MatchNode;
|
||||||
|
data_match.actions["n2"] = ClauseAction::MatchNode;
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
generator.add_action(QueryAction::Create);
|
||||||
|
|
||||||
|
auto& data_create = generator.action_data();
|
||||||
|
data_create.actions["r"] = ClauseAction::CreateRelationship;
|
||||||
|
|
||||||
|
// RETURN
|
||||||
|
generator.add_action(QueryAction::Return);
|
||||||
|
|
||||||
|
auto& data_return = generator.action_data();
|
||||||
|
data_return.actions["r"] = ClauseAction::ReturnRelationship;
|
||||||
|
|
||||||
|
generator.generate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,143 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "query_engine/hardcode/queries.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create account
|
|
||||||
// FULL: CREATE (n:ACCOUNT {id: 50, name: "Nikola", country: "Croatia",
|
|
||||||
// created_at: 14634563}) RETURN n
|
|
||||||
// STRIPPED: CREATE(n:ACCOUNT{id:0,name:1,country:2,created_at:3})RETURNn
|
|
||||||
// HASH: 10597108978382323595
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create personnel
|
|
||||||
// FULL: CREATE (n:PERSONNEL {id: 23, role: "CTO", created_at: 1235345})
|
|
||||||
// RETURN n
|
|
||||||
// STRIPPED: CREATE(n:PERSONNEL{id:0,role:1,created_at:2})RETURNn
|
|
||||||
// HASH: 4037885257628527960
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create edge between ACCOUNT node and PERSONNEL node (IS type)
|
|
||||||
// FULL: MATCH (a:ACCOUNT {id:50}), (p:PERSONNEL {id: 23}) CREATE
|
|
||||||
// (a)-[:IS]->(p)
|
|
||||||
// STRIPPED: MATCH(a:ACCOUNT{id:0}),(p:PERSONNEL{id:1})CREATE(a)-[:IS]->(p)
|
|
||||||
// HASH: 16888190822925675190
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: find ACCOUNT node, PERSONNEL node and edge between them
|
|
||||||
// FULL: MATCH (a:ACCOUNT {id:50})-[r:IS]->(p:PERSONNEL {id: 23}) RETURN
|
|
||||||
// a,r,p
|
|
||||||
// STRIPPED: MATCH(a:ACCOUNT{id:0})-[r:IS]->(p:PERSONNEL{id:1})RETURNa,r,p
|
|
||||||
// HASH: 9672752533852902744
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create OPPORTUNITY
|
|
||||||
// FULL:
|
|
||||||
// STRIPPED:
|
|
||||||
// HASH:
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create PERSONNEL-[:CREATED]->OPPORTUNITY
|
|
||||||
// FULL:
|
|
||||||
// STRIPPED:
|
|
||||||
// HASH:
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create COMPANY
|
|
||||||
// FULL:
|
|
||||||
// STRIPPED:
|
|
||||||
// HASH:
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create OPPORTUNITY-[:MATCH]->COMPANY
|
|
||||||
// FULL:
|
|
||||||
// STRIPPED:
|
|
||||||
// HASH:
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: create an edge between two nodes that are found by the ID
|
|
||||||
// FULL: MATCH (a {id:0}), (p {id: 1}) CREATE (a)-[r:IS]->(p) RETURN r
|
|
||||||
// STRIPPED: MATCH(a{id:0}),(p{id:1})CREATE(a)-[r:IS]->(p)RETURNr
|
|
||||||
// HASH: 7939106225150551899
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: fine node by the ID
|
|
||||||
// FULL: MATCH (n {id: 0}) RETURN n
|
|
||||||
// STRIPPED: MATCH(n{id:0})RETURNn
|
|
||||||
// HASH: 11198568396549106428
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: find edge by the ID
|
|
||||||
// FULL: MATCH ()-[r]-() WHERE ID(r)=0 RETURN r
|
|
||||||
// STRIPPED: MATCH()-[r]-()WHEREID(r)=0RETURNr
|
|
||||||
// HASH: 8320600413058284114
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: update node that is found by the ID
|
|
||||||
// FULL: MATCH (n: {id: 0}) SET n.name = "TEST100" RETURN n
|
|
||||||
// STRIPPED: MATCH(n:{id:0})SETn.name=1RETURNn
|
|
||||||
// HASH: 6813335159006269041
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: find shortest path between two nodes specified by ID
|
|
||||||
// FULL:
|
|
||||||
// STRIPPED:
|
|
||||||
// HASH:
|
|
||||||
// STATUS: TODO
|
|
||||||
// --
|
|
||||||
// DESCRIPTION: find all nodes by label name
|
|
||||||
// FULL: MATCH (n:LABEL) RETURN n
|
|
||||||
// STRIPPPED: MATCH(n:LABEL)RETURNn
|
|
||||||
// HASH: 4857652843629217005
|
|
||||||
// STATUS: DONE
|
|
||||||
// --
|
|
||||||
// TODO: Labels and Types have to be extracted from a query during the
|
|
||||||
// query compile time
|
|
||||||
|
|
||||||
// void cout_properties(const Properties &properties)
|
|
||||||
// {
|
|
||||||
// ConsoleWriter writer;
|
|
||||||
// properties.accept(writer);
|
|
||||||
// }
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
Db db;
|
|
||||||
auto queries = load_queries(db);
|
|
||||||
|
|
||||||
// auto arguments = all_arguments(argc, argv);
|
|
||||||
// auto input_query = extract_query(arguments);
|
|
||||||
auto stripper = make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL);
|
|
||||||
// auto stripped = stripper.strip(input_query);
|
|
||||||
//
|
|
||||||
// auto time = timer<ms>([&stripped, &queries]() {
|
|
||||||
// for (int i = 0; i < 1000000; ++i) {
|
|
||||||
// queries[stripped.hash](stripped.arguments);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// std::cout << time << std::endl;
|
|
||||||
|
|
||||||
vector<string> history;
|
|
||||||
string command;
|
|
||||||
cout << "-- Memgraph query engine --" << endl;
|
|
||||||
do {
|
|
||||||
|
|
||||||
cout << "> ";
|
|
||||||
getline(cin, command);
|
|
||||||
history.push_back(command);
|
|
||||||
auto stripped = stripper.strip(command);
|
|
||||||
|
|
||||||
if (queries.find(stripped.hash) == queries.end()) {
|
|
||||||
cout << "unsupported query" << endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = queries[stripped.hash](stripped.arguments);
|
|
||||||
cout << "RETURN: " << result << endl;
|
|
||||||
|
|
||||||
} while (command != "quit");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#define DEBUG 1
|
|
||||||
|
|
||||||
#include "utils/command_line/arguments.hpp"
|
|
||||||
#include "cypher/common.hpp"
|
|
||||||
#include "query_engine.hpp"
|
|
||||||
#include "utils/time/timer.hpp"
|
|
||||||
|
|
||||||
using std::cout;
|
|
||||||
using std::endl;
|
|
||||||
using std::cin;
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
// arguments parsing
|
|
||||||
auto arguments = all_arguments(argc, argv);
|
|
||||||
|
|
||||||
// query extraction
|
|
||||||
auto cypher_query = extract_query(arguments);
|
|
||||||
cout << "QUERY: " << cypher_query << endl;
|
|
||||||
QueryEngine engine;
|
|
||||||
engine.execute(cypher_query);
|
|
||||||
|
|
||||||
// using std::placeholders::_1;
|
|
||||||
// auto f = std::bind(&QueryEngine::execute, &engine, _1);
|
|
||||||
|
|
||||||
// cout << std::fixed << timer(f, cypher_query) << endl;
|
|
||||||
|
|
||||||
// double counter = 0;
|
|
||||||
// for (int i = 0; i < 1000000; ++i) {
|
|
||||||
// counter += timer(f, cypher_query);
|
|
||||||
// }
|
|
||||||
// cout << 1000000 / (counter / 1000000000) << "create_transactions per sec" << endl;
|
|
||||||
|
|
||||||
// shell
|
|
||||||
std::string command;
|
|
||||||
cout << "-- Memgraph query engine --" << endl;
|
|
||||||
do {
|
|
||||||
cout << "> ";
|
|
||||||
std::getline(cin, command);
|
|
||||||
engine.execute(command);
|
|
||||||
} while (command != "quit");
|
|
||||||
|
|
||||||
// engine.execute("CREATE (n{id:2}) RETURN n");
|
|
||||||
// engine.execute("MATCH (n{id:0}) RETURN n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
45
tests/manual/queries.cpp
Normal file
45
tests/manual/queries.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "query_engine/hardcode/queries.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Db db;
|
||||||
|
auto queries = load_queries(db);
|
||||||
|
|
||||||
|
// auto arguments = all_arguments(argc, argv);
|
||||||
|
// auto input_query = extract_query(arguments);
|
||||||
|
auto stripper = make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL);
|
||||||
|
// auto stripped = stripper.strip(input_query);
|
||||||
|
//
|
||||||
|
// auto time = timer<ms>([&stripped, &queries]() {
|
||||||
|
// for (int i = 0; i < 1000000; ++i) {
|
||||||
|
// queries[stripped.hash](stripped.arguments);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// std::cout << time << std::endl;
|
||||||
|
|
||||||
|
vector<string> history;
|
||||||
|
string command;
|
||||||
|
cout << "-- Memgraph query engine --" << endl;
|
||||||
|
do {
|
||||||
|
|
||||||
|
cout << "> ";
|
||||||
|
getline(cin, command);
|
||||||
|
history.push_back(command);
|
||||||
|
auto stripped = stripper.strip(command);
|
||||||
|
|
||||||
|
if (queries.find(stripped.hash) == queries.end()) {
|
||||||
|
cout << "unsupported query" << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = queries[stripped.hash](stripped.arguments);
|
||||||
|
cout << "RETURN: " << result << endl;
|
||||||
|
|
||||||
|
} while (command != "quit");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "query_hasher.hpp"
|
|
||||||
#include "cypher/common.hpp"
|
#include "cypher/common.hpp"
|
||||||
#include "query_stripper.hpp"
|
#include "query_engine/query_hasher.hpp"
|
||||||
|
#include "query_engine/query_stripper.hpp"
|
||||||
#include "utils/command_line/arguments.hpp"
|
#include "utils/command_line/arguments.hpp"
|
||||||
#include "utils/type_discovery.hpp"
|
#include "utils/type_discovery.hpp"
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
cout << "QUERY: " << input_query << endl;
|
cout << "QUERY: " << input_query << endl;
|
||||||
|
|
||||||
auto stripper = make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL);
|
auto stripper = make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL);
|
||||||
auto stripped = stripper.strip(input_query);
|
auto stripped = stripper.strip(input_query);
|
||||||
|
|
||||||
cout << "STRIPPED QUERY: " << stripped.query << endl;
|
cout << "STRIPPED QUERY: " << stripped.query << endl;
|
@ -1,6 +1,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "query_engine/state_machine/cypher.hpp"
|
#include "query_engine/code_generator/entity_search.hpp"
|
||||||
#include "utils/assert.hpp"
|
#include "utils/assert.hpp"
|
||||||
#include "utils/underlying_cast.hpp"
|
#include "utils/underlying_cast.hpp"
|
||||||
|
|
||||||
|
19
tests/unit/parameter_index.cpp
Normal file
19
tests/unit/parameter_index.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "query_engine/code_generator/query_action_data.hpp"
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
|
using ParameterIndexKey::Type::InternalId;
|
||||||
|
using ParameterIndexKey::Type::Projection;
|
||||||
|
|
||||||
|
auto main() -> int
|
||||||
|
{
|
||||||
|
std::map<ParameterIndexKey, uint64_t> parameter_index;
|
||||||
|
|
||||||
|
parameter_index[ParameterIndexKey(InternalId, "n1")] = 0;
|
||||||
|
parameter_index[ParameterIndexKey(InternalId, "n2")] = 1;
|
||||||
|
|
||||||
|
permanent_assert(parameter_index.size() == 2, "Parameter index size should be 2");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user