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* 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 Integer;
|
||||
struct String;
|
||||
struct Long;
|
||||
|
||||
// operators
|
||||
struct And;
|
||||
@ -36,7 +37,7 @@ struct Slash;
|
||||
struct Rem;
|
||||
|
||||
struct RelationshipSpecs;
|
||||
struct RelationshipList;
|
||||
struct RelationshipTypeList;
|
||||
struct Relationship;
|
||||
|
||||
struct Node;
|
||||
@ -74,18 +75,15 @@ struct WithQuery;
|
||||
struct InternalIdExpr;
|
||||
|
||||
struct AstVisitor
|
||||
: public Visitor<Accessor, Boolean, Float, Identifier, Alias, Integer,
|
||||
String, Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus,
|
||||
Minus, Star, Slash, Rem, PropertyList, RelationshipList,
|
||||
Relationship, Node, RelationshipSpecs, LabelList,
|
||||
ReturnList, Pattern, PatternExpr, PatternList, Match,
|
||||
ReadQuery, Start, Where, WriteQuery, Create, Return,
|
||||
Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey,
|
||||
ReadWriteQuery, IdentifierList,
|
||||
WithList, WithClause, WithQuery,
|
||||
InternalIdExpr,
|
||||
SetValue, SetElement, SetList>
|
||||
: public Visitor<
|
||||
Accessor, Boolean, Float, Identifier, Alias, Integer, String,
|
||||
Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus, Minus, Star, Slash,
|
||||
Rem, PropertyList, RelationshipTypeList, Relationship, Node,
|
||||
RelationshipSpecs, LabelList, ReturnList, Pattern, PatternExpr,
|
||||
PatternList, Match, ReadQuery, Start, Where, WriteQuery, Create,
|
||||
Return, Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey,
|
||||
ReadWriteQuery, IdentifierList, WithList, WithClause, WithQuery, Long,
|
||||
InternalIdExpr, SetValue, SetElement, SetList>
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ struct List : public AstNode<Derived>
|
||||
|
||||
T* value;
|
||||
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;
|
||||
LabelList* labels;
|
||||
PropertyList* props;
|
||||
|
||||
bool has_identifier() const
|
||||
{
|
||||
return idn != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,25 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "relationship.hpp"
|
||||
#include "node.hpp"
|
||||
#include "relationship.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Pattern : public AstNode<Pattern>
|
||||
{
|
||||
Pattern(Node* node, Relationship* relationship, Pattern* next)
|
||||
: node(node), relationship(relationship), next(next) {}
|
||||
Pattern(Node *node, Relationship *relationship, Pattern *next)
|
||||
: node(node), relationship(relationship), next(next)
|
||||
{
|
||||
}
|
||||
|
||||
Node* node;
|
||||
Relationship* relationship;
|
||||
Pattern* next;
|
||||
Node *node;
|
||||
Relationship *relationship;
|
||||
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>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ struct Property : public AstNode<Property>
|
||||
|
||||
Identifier* idn;
|
||||
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>
|
||||
|
@ -1,35 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct RelationshipList : public List<Identifier, RelationshipList>
|
||||
struct RelationshipTypeList : public List<Identifier, RelationshipTypeList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct RelationshipSpecs : public AstNode<RelationshipSpecs>
|
||||
{
|
||||
RelationshipSpecs(Identifier* idn, RelationshipList* types, PropertyList* props)
|
||||
: idn(idn), types(types), props(props) {}
|
||||
RelationshipSpecs(Identifier *idn, RelationshipTypeList *types,
|
||||
PropertyList *props)
|
||||
: idn(idn), types(types), props(props)
|
||||
{
|
||||
}
|
||||
|
||||
Identifier* idn;
|
||||
RelationshipList* types;
|
||||
PropertyList* props;
|
||||
Identifier *idn;
|
||||
RelationshipTypeList *types;
|
||||
PropertyList *props;
|
||||
|
||||
bool has_identifier() const { return idn != nullptr; }
|
||||
|
||||
std::string name() const { return idn->name; }
|
||||
};
|
||||
|
||||
struct Relationship : public AstNode<Relationship>
|
||||
{
|
||||
enum Direction { Left, Right, Both };
|
||||
enum Direction
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
|
||||
Relationship(RelationshipSpecs* specs, Direction direction)
|
||||
: specs(specs), direction(direction) {}
|
||||
Relationship(RelationshipSpecs *specs, Direction direction)
|
||||
: specs(specs), direction(direction)
|
||||
{
|
||||
}
|
||||
|
||||
RelationshipSpecs* specs;
|
||||
RelationshipSpecs *specs;
|
||||
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) {}
|
||||
|
||||
Expr* value;
|
||||
|
||||
bool has_value() const { return value != nullptr; }
|
||||
};
|
||||
|
||||
struct SetElement : public AstNode<SetElement>
|
||||
@ -40,6 +42,9 @@ struct SetElement : public AstNode<SetElement>
|
||||
|
||||
Accessor* accessor;
|
||||
SetValue* set_value;
|
||||
|
||||
bool has_accessor() const { return accessor != nullptr; }
|
||||
bool has_value() const { return set_value != nullptr; }
|
||||
};
|
||||
|
||||
struct SetList : public List<SetElement, SetList>
|
||||
|
@ -3,8 +3,6 @@
|
||||
#include "ast_node.hpp"
|
||||
#include "queries.hpp"
|
||||
|
||||
// LEGACY TODO remove from here
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
|
@ -39,15 +39,23 @@ struct String : public LeafExpr<std::string, String>
|
||||
|
||||
struct InternalIdExpr : public Expr
|
||||
{
|
||||
InternalIdExpr(Identifier *identifier, Integer *value)
|
||||
: identifier(identifier), value(value)
|
||||
InternalIdExpr(Identifier *entity, Long *id)
|
||||
: entity(entity), id(id)
|
||||
{
|
||||
}
|
||||
|
||||
Identifier *identifier;
|
||||
Integer *value;
|
||||
Identifier *entity;
|
||||
Long *id;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
%type rel_type {ast::RelationshipList*}
|
||||
%type rel_type {ast::RelationshipTypeList*}
|
||||
|
||||
rel_type(L) ::= COLON rel_list(R). {
|
||||
L = R;
|
||||
@ -274,14 +274,14 @@ rel_type(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
%type rel_list {ast::RelationshipList*}
|
||||
%type rel_list {ast::RelationshipTypeList*}
|
||||
|
||||
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). {
|
||||
L = ast->create<ast::RelationshipList>(I, nullptr);
|
||||
L = ast->create<ast::RelationshipTypeList>(I, nullptr);
|
||||
}
|
||||
|
||||
%type node {ast::Node*}
|
||||
@ -444,9 +444,9 @@ value_expr(E) ::= idn(I) DOT idn(P). {
|
||||
E = ast->create<ast::Accessor>(I, P);
|
||||
}
|
||||
|
||||
value_expr(E) ::= ID LP idn(I) RP EQ INT(V). {
|
||||
auto value = std::stoi(V->value);
|
||||
E = ast->create<ast::InternalIdExpr>(I, ast->create<ast::Integer>(value));
|
||||
value_expr(E) ::= ID LP idn(I) RP EQ LONG(V). {
|
||||
auto value = std::stol(V->value);
|
||||
E = ast->create<ast::InternalIdExpr>(I, ast->create<ast::Long>(value));
|
||||
}
|
||||
|
||||
%type idn {ast::Identifier*}
|
||||
@ -469,9 +469,9 @@ identifier_list(L) ::= idn(I). {
|
||||
|
||||
// ----
|
||||
|
||||
value_expr(E) ::= INT(V). {
|
||||
auto value = std::stoi(V->value);
|
||||
E = ast->create<ast::Integer>(value);
|
||||
value_expr(E) ::= LONG(V). {
|
||||
auto value = std::stol(V->value);
|
||||
E = ast->create<ast::Long>(value);
|
||||
}
|
||||
|
||||
value_expr(E) ::= FLOAT(V). {
|
||||
|
@ -184,6 +184,12 @@ public:
|
||||
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
|
||||
// {
|
||||
// auto entry = printer.advance();
|
||||
@ -292,9 +298,9 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
rule("\\\"(.*?)\\\"", TK_STR);
|
||||
|
||||
// number
|
||||
rule("\\d+", TK_INT);
|
||||
rule("\\d+", TK_LONG);
|
||||
rule("\\d*[.]?\\d+", TK_FLOAT);
|
||||
|
||||
// identifier
|
||||
|
@ -166,7 +166,7 @@ public:
|
||||
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.next);
|
||||
@ -278,8 +278,8 @@ public:
|
||||
|
||||
void visit(ast::InternalIdExpr& internal_id) override
|
||||
{
|
||||
accept(internal_id.identifier);
|
||||
accept(internal_id.value);
|
||||
accept(internal_id.entity);
|
||||
accept(internal_id.id);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "exceptions/query_engine_exception.hpp"
|
||||
#include "exceptions/exceptions.hpp"
|
||||
#include "utils/string/join.hpp"
|
||||
|
||||
// TODO:
|
||||
@ -32,11 +32,14 @@ public:
|
||||
|
||||
// if compilation has failed throw exception
|
||||
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
|
||||
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 "cypher/ast/ast.hpp"
|
||||
#include "cypher/compiler.hpp"
|
||||
#include "query_engine/exceptions/exceptions.hpp"
|
||||
#include "template_engine/engine.hpp"
|
||||
#include "traverser/code_traverser.hpp"
|
||||
#include "traverser/cpp_traverser.hpp"
|
||||
#include "utils/string/file.hpp"
|
||||
#include "query_engine/exceptions/query_engine_exception.hpp"
|
||||
|
||||
// TODO:
|
||||
// * logger
|
||||
@ -20,6 +20,11 @@ public:
|
||||
void generate_cpp(const std::string &query, const uint64_t stripped_hash,
|
||||
const std::string &path)
|
||||
{
|
||||
// TODO: optimize initialize only once -> be careful that object has
|
||||
// a state
|
||||
// TODO: multithread test
|
||||
CppTraverser cpp_traverser;
|
||||
|
||||
// get paths
|
||||
string template_path = CONFIG(config::TEMPLATE_CPU_CPP_PATH);
|
||||
string template_file = utils::read_file(template_path.c_str());
|
||||
@ -32,13 +37,14 @@ public:
|
||||
throw QueryEngineException("Syntax tree generation error");
|
||||
}
|
||||
|
||||
code_traverser.reset();
|
||||
cpp_traverser.reset();
|
||||
|
||||
// code generation
|
||||
try {
|
||||
tree.root->accept(code_traverser);
|
||||
tree.root->accept(cpp_traverser);
|
||||
} catch (const SemanticException &e) {
|
||||
throw e;
|
||||
} catch (const std::exception &e) {
|
||||
// TODO: extract more information
|
||||
throw QueryEngineException("Code generation error");
|
||||
}
|
||||
|
||||
@ -47,7 +53,7 @@ public:
|
||||
template_file, {{"class_name", "CodeCPU"},
|
||||
{"stripped_hash", std::to_string(stripped_hash)},
|
||||
{"query", query},
|
||||
{"code", code_traverser.code}});
|
||||
{"code", cpp_traverser.code}});
|
||||
|
||||
// TODO: use logger, ifndef
|
||||
std::cout << generated << std::endl;
|
||||
@ -59,5 +65,4 @@ private:
|
||||
template_engine::TemplateEngine template_engine;
|
||||
ast::Ast tree;
|
||||
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
|
||||
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();
|
||||
}
|
||||
|
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);
|
||||
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);
|
||||
v2.vlist->update(t)->data.in.add(e.vlist);
|
||||
v1.vlist->update(t)->data.out.add(edge_accessor.vlist);
|
||||
v2.vlist->update(t)->data.in.add(edge_accessor.vlist);
|
||||
|
||||
e.from(v1.vlist);
|
||||
e.to(v2.vlist);
|
||||
edge_accessor.from(v1.vlist);
|
||||
edge_accessor.to(v2.vlist);
|
||||
|
||||
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();
|
||||
|
||||
cout << e.edge_type() << endl;
|
||||
cout_properties(e.properties());
|
||||
cout << edge_accessor.edge_type() << endl;
|
||||
|
||||
cout_properties(edge_accessor.properties());
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto find_edge_by_internal_id = [&db](const properties_t &args) {
|
||||
auto &t = db.tx_engine.begin();
|
||||
auto e = db.graph.edges.find(t, args[0]->as<Int32>().value);
|
||||
if (!e) return t.commit(), false;
|
||||
auto edge_accessor = db.graph.edges.find(t, args[0]->as<Int32>().value);
|
||||
if (!edge_accessor) return t.commit(), false;
|
||||
|
||||
// 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_properties(from->find(t)->data.props);
|
||||
|
||||
auto to = e.to();
|
||||
auto to = edge_accessor.to();
|
||||
cout << "TO:" << endl;
|
||||
cout_properties(to->find(t)->data.props);
|
||||
|
||||
@ -139,6 +140,7 @@ auto load_queries(Db& db)
|
||||
queries[10597108978382323595u] = create_account;
|
||||
queries[5397556489557792025u] = create_labeled_and_named_node;
|
||||
queries[7939106225150551899u] = create_edge;
|
||||
queries[6579425155585886196u] = create_edge;
|
||||
queries[11198568396549106428u] = find_node_by_internal_id;
|
||||
queries[8320600413058284114u] = find_edge_by_internal_id;
|
||||
queries[6813335159006269041u] = update_node;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "query_engine/util.hpp"
|
||||
#include "query_program.hpp"
|
||||
#include "utils/log/logger.hpp"
|
||||
#include "query_engine/exceptions/query_engine_exception.hpp"
|
||||
#include "query_engine/exceptions/exceptions.hpp"
|
||||
|
||||
// preparations before execution
|
||||
// execution
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
using sptr_code_lib = std::shared_ptr<CodeLib>;
|
||||
|
||||
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
|
||||
auto base_path = config::Config::instance()[config::COMPILE_CPU_PATH];
|
||||
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";
|
||||
code_compiler.compile(path_cpp, path_so);
|
||||
|
@ -9,6 +9,11 @@
|
||||
// query -> code_loader -> query_stripper -> [code_generator]
|
||||
// -> [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
|
||||
{
|
||||
public:
|
||||
|
@ -8,7 +8,7 @@ class QueryHasher
|
||||
{
|
||||
public:
|
||||
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)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ struct QueryStripped
|
||||
QueryStripped(QueryStripped &other) = delete;
|
||||
QueryStripped(QueryStripped &&other) = default;
|
||||
|
||||
code_args_t arguments;
|
||||
uint64_t hash;
|
||||
std::string query;
|
||||
uint64_t hash;
|
||||
code_args_t arguments;
|
||||
};
|
||||
|
@ -8,13 +8,12 @@
|
||||
|
||||
#include "cypher/cypher.h"
|
||||
#include "cypher/tokenizer/cypher_lexer.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
#include "query_stripped.hpp"
|
||||
#include "storage/model/properties/all.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
#include "utils/string/transform.hpp"
|
||||
#include "utils/variadic/variadic.hpp"
|
||||
|
||||
|
||||
template <class T, class 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
|
||||
// concatenation with something smarter)
|
||||
@ -60,9 +65,9 @@ public:
|
||||
if (_or(token.id, strip_types, std::make_index_sequence<size>{})) {
|
||||
auto index = counter++;
|
||||
switch (token.id) {
|
||||
case TK_INT:
|
||||
store_query_param<Int32>(stripped_arguments,
|
||||
std::stoi(token.value));
|
||||
case TK_LONG:
|
||||
store_query_param<Int64>(stripped_arguments,
|
||||
std::stol(token.value));
|
||||
break;
|
||||
case TK_STR:
|
||||
store_query_param<String>(stripped_arguments, token.value);
|
||||
@ -77,15 +82,14 @@ public:
|
||||
std::stof(token.value));
|
||||
break;
|
||||
}
|
||||
stripped_query += std::to_string(index);
|
||||
stripped_query += std::to_string(index) + separator;
|
||||
} else {
|
||||
// TODO: lowercase only keywords like (MATCH, CREATE, ...)
|
||||
stripped_query += token.value;
|
||||
stripped_query += token.value + separator;
|
||||
}
|
||||
}
|
||||
|
||||
return QueryStripped(std::move(stripped_query),
|
||||
fnv(stripped_query),
|
||||
return QueryStripped(std::move(stripped_query), fnv(stripped_query),
|
||||
std::move(stripped_arguments));
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,6 @@
|
||||
#include "query_engine/i_code_cpu.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::endl;
|
||||
|
||||
|
@ -28,12 +28,22 @@ const std::string args_id = "auto id = args[{}]->as<Int32>();";
|
||||
const std::string vertex_accessor_args_id =
|
||||
"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 =
|
||||
"vertex_accessor.property(\"{}\", args[{}]);";
|
||||
|
||||
const std::string return_empty_result =
|
||||
"return std::make_shared<QueryResult>();";
|
||||
|
||||
const std::string update_property =
|
||||
"{}.property(\"{}\", args[{}]);";
|
||||
|
||||
const std::string create_label =
|
||||
"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 "cypher/visitor/traverser.hpp"
|
||||
#include "query_engine/state_machine/cypher.hpp"
|
||||
#include "query_engine/util.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
#include "query_engine/code_generator/entity_search.hpp"
|
||||
|
||||
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
|
||||
|
||||
# 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})
|
||||
target_link_libraries(manual_query_engine ${fmt_static_lib})
|
||||
target_link_libraries(manual_query_engine dl)
|
||||
target_link_libraries(manual_query_engine cypher_lib)
|
||||
set_property(TARGET manual_query_engine PROPERTY CXX_STANDARD 14)
|
||||
|
||||
# ast traversal
|
||||
add_executable(cypher_ast_traverser manual/cypher_ast.cpp ${mamgraph_src_files})
|
||||
target_link_libraries(cypher_ast_traverser ${fmt_static_lib})
|
||||
target_link_libraries(cypher_ast_traverser cypher_lib)
|
||||
set_property(TARGET cypher_ast_traverser PROPERTY CXX_STANDARD 14)
|
||||
# query_hasher
|
||||
add_executable(manual_query_hasher manual/query_hasher.cpp ${memgraph_src_files})
|
||||
target_link_libraries(manual_query_hasher ${fmt_static_lib})
|
||||
set_property(TARGET manual_query_hasher 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 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
|
||||
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 "query_hasher.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/type_discovery.hpp"
|
||||
|
||||
@ -20,7 +20,7 @@ int main(int argc, char** argv)
|
||||
|
||||
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);
|
||||
|
||||
cout << "STRIPPED QUERY: " << stripped.query << endl;
|
@ -1,6 +1,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "query_engine/state_machine/cypher.hpp"
|
||||
#include "query_engine/code_generator/entity_search.hpp"
|
||||
#include "utils/assert.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