Query engine work in progress: code_generator handlers, update clause generator, delete clause generator, return clause generator, SemanticError
This commit is contained in:
parent
356e9444ec
commit
6970170f69
@ -20,8 +20,8 @@ struct Accessor : public ValueExpr<Accessor>
|
||||
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; }
|
||||
std::string &entity_name() const { return entity->name; }
|
||||
std::string &entity_prop() const { return prop->name; }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,20 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include "token.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
// TODO: optimaze exceptions in respect to
|
||||
// query_engine/exceptions/error.hpp
|
||||
|
||||
class SyntaxError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
SyntaxError(const std::string& near)
|
||||
: std::runtime_error("Syntax error near '" + near + "'.") {}
|
||||
SyntaxError(const std::string &near)
|
||||
: std::runtime_error("Syntax error: near '" + near + "'.")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class LexicalError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
LexicalError(const Token& token)
|
||||
: std::runtime_error("Unrecognized token '" + token.value + "'.") {}
|
||||
LexicalError(const Token &token)
|
||||
: std::runtime_error("Lexical error: unrecognized token '" +
|
||||
token.value + "'.")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ParserError : public std::runtime_error
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "config/config.hpp"
|
||||
#include "cypher/ast/ast.hpp"
|
||||
#include "cypher/compiler.hpp"
|
||||
#include "query_engine/exceptions/exceptions.hpp"
|
||||
#include "query_engine/exceptions/errors.hpp"
|
||||
#include "template_engine/engine.hpp"
|
||||
#include "traverser/cpp_traverser.hpp"
|
||||
#include "utils/string/file.hpp"
|
||||
@ -20,8 +20,8 @@ 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: optimize; one time initialization -> be careful that object
|
||||
// has a state
|
||||
// TODO: multithread test
|
||||
CppTraverser cpp_traverser;
|
||||
|
||||
@ -32,9 +32,8 @@ public:
|
||||
// syntax tree generation
|
||||
try {
|
||||
tree = compiler.syntax_tree(query);
|
||||
} catch (const std::exception &e) {
|
||||
// TODO: extract more information
|
||||
throw QueryEngineException("Syntax tree generation error");
|
||||
} catch (const std::runtime_error &e) {
|
||||
throw QueryEngineException(std::string(e.what()));
|
||||
}
|
||||
|
||||
cpp_traverser.reset();
|
||||
@ -42,10 +41,10 @@ public:
|
||||
// code generation
|
||||
try {
|
||||
tree.root->accept(cpp_traverser);
|
||||
} catch (const SemanticException &e) {
|
||||
} catch (const SemanticError &e) {
|
||||
throw e;
|
||||
} catch (const std::exception &e) {
|
||||
throw QueryEngineException("Code generation error");
|
||||
throw QueryEngineException("Unknown code generation error");
|
||||
}
|
||||
|
||||
// save the code
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "query_engine/code_generator/cypher_state.hpp"
|
||||
#include "query_engine/code_generator/handlers/all.hpp"
|
||||
#include "query_engine/code_generator/query_action.hpp"
|
||||
#include "query_engine/exceptions/exceptions.hpp"
|
||||
|
||||
@ -66,6 +67,7 @@ private:
|
||||
action_functions[QueryAction::Match] = match_query_action;
|
||||
action_functions[QueryAction::Return] = return_query_action;
|
||||
action_functions[QueryAction::Set] = set_query_action;
|
||||
action_functions[QueryAction::Delete] = delete_query_action;
|
||||
action_functions[QueryAction::TransactionCommit] =
|
||||
transaction_commit_action;
|
||||
}
|
||||
|
9
src/query_engine/code_generator/handlers/all.hpp
Normal file
9
src/query_engine/code_generator/handlers/all.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/create.hpp"
|
||||
#include "query_engine/code_generator/handlers/match.hpp"
|
||||
#include "query_engine/code_generator/handlers/return.hpp"
|
||||
#include "query_engine/code_generator/handlers/set.hpp"
|
||||
#include "query_engine/code_generator/handlers/delete.hpp"
|
||||
#include "query_engine/code_generator/handlers/transaction_begin.hpp"
|
||||
#include "query_engine/code_generator/handlers/transaction_commit.hpp"
|
39
src/query_engine/code_generator/handlers/create.hpp
Normal file
39
src/query_engine/code_generator/handlers/create.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
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::CreateNode) {
|
||||
// 1. create node 2. update labels 3. update properties
|
||||
auto &name = kv.first;
|
||||
code += LINE(fmt::format(code::create_vertex, name));
|
||||
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::set_vertex_property, name,
|
||||
property, index));
|
||||
}
|
||||
for (auto &label : entity_data.tags) {
|
||||
code += LINE(fmt::format(code::create_label, label));
|
||||
code += LINE(fmt::format(code::add_label, name, label));
|
||||
}
|
||||
cypher_data.node_created(name);
|
||||
}
|
||||
|
||||
if (kv.second == ClauseAction::CreateRelationship) {
|
||||
auto name = kv.first;
|
||||
code += LINE(fmt::format(code::create_edge, name));
|
||||
cypher_data.relationship_created(name);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
22
src/query_engine/code_generator/handlers/delete.hpp
Normal file
22
src/query_engine/code_generator/handlers/delete.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
auto delete_query_action =
|
||||
[](CypherStateData &cypher_data,
|
||||
const QueryActionData &action_data) -> std::string {
|
||||
|
||||
std::string code = "";
|
||||
|
||||
for (auto const &kv : action_data.actions) {
|
||||
auto entity = kv.first;
|
||||
if (kv.second == ClauseAction::DeleteNode) {
|
||||
code += LINE(fmt::format("// DELETE Node({})", entity));
|
||||
}
|
||||
if (kv.second == ClauseAction::DeleteRelationship) {
|
||||
code += LINE(fmt::format("// DELETE Relationship({})", entity));
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
14
src/query_engine/code_generator/handlers/includes.hpp
Normal file
14
src/query_engine/code_generator/handlers/includes.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#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"
|
||||
#include "query_engine/exceptions/errors.hpp"
|
||||
|
||||
using ParameterIndexKey::Type::InternalId;
|
58
src/query_engine/code_generator/handlers/match.hpp
Normal file
58
src/query_engine/code_generator/handlers/match.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
bool already_matched(CypherStateData &cypher_data, const std::string &name,
|
||||
EntityType type)
|
||||
{
|
||||
if (cypher_data.type(name) == type &&
|
||||
cypher_data.status(name) == EntityStatus::Matched)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fetch_internal_index(const QueryActionData &action_data,
|
||||
const std::string &name)
|
||||
{
|
||||
return action_data.parameter_index.at(ParameterIndexKey(InternalId, name));
|
||||
}
|
||||
|
||||
auto match_query_action =
|
||||
[](CypherStateData &cypher_data,
|
||||
const QueryActionData &action_data) -> std::string {
|
||||
|
||||
std::string code = "";
|
||||
|
||||
for (auto const &kv : action_data.actions) {
|
||||
|
||||
// TODO: the same code REFACTOR!
|
||||
// find node
|
||||
if (kv.second == ClauseAction::MatchNode) {
|
||||
auto name = kv.first;
|
||||
if (already_matched(cypher_data, name, EntityType::Node)) continue;
|
||||
auto place = action_data.csm.min(kv.first);
|
||||
if (place == entity_search::search_internal_id) {
|
||||
auto index = fetch_internal_index(action_data, name);
|
||||
code +=
|
||||
LINE(fmt::format(code::match_vertex_by_id, name, index));
|
||||
cypher_data.node_matched(name);
|
||||
}
|
||||
}
|
||||
|
||||
// find relationship
|
||||
if (kv.second == ClauseAction::MatchRelationship) {
|
||||
auto name = kv.first;
|
||||
if (already_matched(cypher_data, name, EntityType::Relationship))
|
||||
continue;
|
||||
auto place = action_data.csm.min(kv.first);
|
||||
if (place == entity_search::search_internal_id) {
|
||||
auto index = fetch_internal_index(action_data, name);
|
||||
code += LINE(fmt::format(code::match_edge_by_id, name, index));
|
||||
cypher_data.relationship_matched(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
30
src/query_engine/code_generator/handlers/return.hpp
Normal file
30
src/query_engine/code_generator/handlers/return.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
auto return_query_action =
|
||||
[](CypherStateData &cypher_data,
|
||||
const QueryActionData &action_data) -> std::string {
|
||||
|
||||
std::string code = "";
|
||||
|
||||
const auto &elements = action_data.return_elements;
|
||||
code += LINE(fmt::format("// number of elements {}", elements.size()));
|
||||
|
||||
// TODO: call bolt serialization
|
||||
for (const auto& element : elements) {
|
||||
auto &entity = element.entity;
|
||||
if (!cypher_data.exist(entity)) {
|
||||
throw SemanticError(
|
||||
fmt::format("{} couldn't be found (RETURN clause).", entity));
|
||||
}
|
||||
if (element.is_entity_only()) {
|
||||
code += LINE(fmt::format("// RETURN: {}", entity));
|
||||
} else if (element.is_projection()) {
|
||||
auto &property = element.property;
|
||||
code += LINE(fmt::format("// RETURN: {}.{}", entity, property));
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
26
src/query_engine/code_generator/handlers/set.hpp
Normal file
26
src/query_engine/code_generator/handlers/set.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
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;
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
auto transaction_begin_action = [](CypherStateData &,
|
||||
const QueryActionData &) -> std::string {
|
||||
return LINE(code::transaction_begin);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "query_engine/code_generator/handlers/includes.hpp"
|
||||
|
||||
auto transaction_commit_action = [](CypherStateData &,
|
||||
const QueryActionData &) -> std::string {
|
||||
return LINE(code::transaction_commit) + LINE(code::return_empty_result);
|
||||
};
|
@ -1,16 +1,6 @@
|
||||
#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;
|
||||
#include <cstdint>
|
||||
|
||||
// any entity (node or relationship) inside cypher query has an action
|
||||
// that is associated with that entity
|
||||
@ -21,90 +11,6 @@ enum class QueryAction : uint32_t
|
||||
Match,
|
||||
Set,
|
||||
Return,
|
||||
Delete,
|
||||
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);
|
||||
};
|
||||
|
@ -95,12 +95,29 @@ struct RelationshipData : public EntityData
|
||||
Direction direction;
|
||||
};
|
||||
|
||||
struct ReturnElement
|
||||
{
|
||||
ReturnElement(const std::string& entity) : entity(entity) {}
|
||||
ReturnElement(const std::string& entity, const std::string& property) :
|
||||
entity(entity), property(property) {};
|
||||
|
||||
std::string entity;
|
||||
std::string property;
|
||||
|
||||
bool has_entity() const { return !entity.empty(); }
|
||||
bool has_property() const { return !property.empty(); }
|
||||
|
||||
bool is_entity_only() const { return has_entity() && !has_property(); }
|
||||
bool is_projection() const { return has_entity() && has_property(); }
|
||||
};
|
||||
|
||||
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;
|
||||
std::vector<ReturnElement> return_elements;
|
||||
CypherStateMachine csm;
|
||||
|
||||
QueryActionData() = default;
|
||||
@ -129,7 +146,7 @@ struct QueryActionData
|
||||
auto get_entity_property(const std::string& entity) const
|
||||
{
|
||||
if (entity_data.find(entity) == entity_data.end())
|
||||
throw CppGeneratorException("Entity doesn't exist");
|
||||
throw CppGeneratorException("Entity " + entity + " doesn't exist");
|
||||
|
||||
return entity_data.at(entity);
|
||||
}
|
||||
|
12
src/query_engine/exceptions/errors.hpp
Normal file
12
src/query_engine/exceptions/errors.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
// TODO: optimaze exceptions in respect to cypher/errors.hpp
|
||||
|
||||
class SemanticError : public BasicException
|
||||
{
|
||||
public:
|
||||
SemanticError(const std::string& what) :
|
||||
BasicException("Semantic error: " + what) {}
|
||||
};
|
@ -7,11 +7,6 @@ class QueryEngineException : public BasicException
|
||||
using BasicException::BasicException;
|
||||
};
|
||||
|
||||
class SemanticException : public BasicException
|
||||
{
|
||||
using BasicException::BasicException;
|
||||
};
|
||||
|
||||
class CppGeneratorException : public BasicException
|
||||
{
|
||||
using BasicException::BasicException;
|
||||
|
@ -20,8 +20,14 @@ const std::string transaction_begin = "auto& t = db.tx_engine.begin();";
|
||||
|
||||
const std::string transaction_commit = "t.commit();";
|
||||
|
||||
const std::string vertex_accessor =
|
||||
"auto vertex_accessor = db.graph.vertices.insert(t);";
|
||||
// create vertex e.g. CREATE (n:PERSON {name: "Test", age: 23})
|
||||
const std::string create_vertex =
|
||||
"auto {} = db.graph.vertices.insert(t);";
|
||||
const std::string set_vertex_property =
|
||||
"{}.property(\"{}\", args[{}]);";
|
||||
const std::string create_label =
|
||||
"auto &{0} = db.graph.label_store.find_or_create(\"{0}\");";
|
||||
const std::string add_label = "{}.add_label({});";
|
||||
|
||||
const std::string args_id = "auto id = args[{}]->as<Int32>();";
|
||||
|
||||
@ -30,13 +36,14 @@ const std::string vertex_accessor_args_id =
|
||||
|
||||
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>();";
|
||||
"if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
|
||||
const std::string match_edge_by_id =
|
||||
"auto {0} = db.graph.edges.find(t, args[{1}]->as<Int64>().value);\n"
|
||||
"if (!{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>();";
|
||||
@ -44,10 +51,6 @@ const std::string return_empty_result =
|
||||
const std::string update_property =
|
||||
"{}.property(\"{}\", args[{}]);";
|
||||
|
||||
const std::string create_label =
|
||||
"auto &{} = db.graph.label_store.find_or_create(\"{}\");";
|
||||
|
||||
const std::string add_label = "vertex_accessor.add_label({});";
|
||||
|
||||
const std::string todo = "// TODO: {}";
|
||||
|
||||
|
@ -86,7 +86,20 @@ private:
|
||||
property_state.clear();
|
||||
}
|
||||
|
||||
// for the first release every query has to have a RETURN clause
|
||||
// problem is where to put t.commit()
|
||||
// TODO: remove this constraint
|
||||
bool has_return;
|
||||
|
||||
public:
|
||||
void semantic_check() const
|
||||
{
|
||||
if (!has_return)
|
||||
throw SemanticError("The query doesn't have RETURN clause. Next "
|
||||
"releases will support query without RETURN "
|
||||
"clause");
|
||||
}
|
||||
|
||||
void visit(ast::WriteQuery &write_query) override
|
||||
{
|
||||
// TODO: crate top level node (TransactionBegin can be
|
||||
@ -153,7 +166,7 @@ public:
|
||||
|
||||
void visit(ast::Set &ast_set) override
|
||||
{
|
||||
code += generator.generate();
|
||||
code += generator.generate();
|
||||
|
||||
generator.add_action(QueryAction::Set);
|
||||
|
||||
@ -165,6 +178,8 @@ public:
|
||||
|
||||
void visit(ast::Return &ast_return) override
|
||||
{
|
||||
has_return = true;
|
||||
|
||||
generator.add_action(QueryAction::Return);
|
||||
|
||||
state = CypherState::Return;
|
||||
@ -180,6 +195,11 @@ public:
|
||||
generator.clear();
|
||||
}
|
||||
|
||||
void visit(ast::ReturnList &ast_return_list) override
|
||||
{
|
||||
Traverser::visit(ast_return_list);
|
||||
}
|
||||
|
||||
void visit(ast::PatternList &ast_pattern_list) override
|
||||
{
|
||||
Traverser::visit(ast_pattern_list);
|
||||
@ -232,7 +252,7 @@ public:
|
||||
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
|
||||
// because entity_id will be value index inside the parameters array
|
||||
auto index = internal_id_expr.entity_id();
|
||||
|
||||
auto &data = generator.action_data();
|
||||
@ -285,6 +305,18 @@ public:
|
||||
|
||||
void visit(ast::RelationshipSpecs &ast_relationship_specs) override
|
||||
{
|
||||
if (state == CypherState::Match) {
|
||||
if (ast_relationship_specs.has_identifier()) {
|
||||
auto name = ast_relationship_specs.name();
|
||||
auto &cypher_data = generator.cypher_data();
|
||||
if (!cypher_data.exist(name)) {
|
||||
clause_action = ClauseAction::MatchRelationship;
|
||||
auto &data = generator.action_data();
|
||||
data.actions[name] = ClauseAction::MatchRelationship;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Traverser::visit(ast_relationship_specs);
|
||||
}
|
||||
|
||||
@ -343,6 +375,16 @@ public:
|
||||
void visit(ast::Identifier &ast_identifier) override
|
||||
{
|
||||
property_state.property_name = ast_identifier.name;
|
||||
|
||||
if (state == CypherState::Delete) {
|
||||
auto &action_data = generator.action_data();
|
||||
auto name = ast_identifier.name;
|
||||
auto &cypher_data = generator.cypher_data();
|
||||
if (cypher_data.type(name) == EntityType::Node)
|
||||
action_data.actions[name] = ClauseAction::DeleteNode;
|
||||
if (cypher_data.type(name) == EntityType::Relationship)
|
||||
action_data.actions[name] = ClauseAction::DeleteRelationship;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(ast::Long &ast_long) override
|
||||
@ -378,8 +420,7 @@ public:
|
||||
auto entity_type = cypher_data.type(entity);
|
||||
|
||||
if (entity_type == EntityType::NotFound)
|
||||
throw SemanticException("Set Entity (" + entity +
|
||||
") doesn't exist");
|
||||
throw SemanticError("Entity (" + entity + ") doesn't exist");
|
||||
|
||||
auto &action_data = generator.action_data();
|
||||
|
||||
@ -388,7 +429,8 @@ public:
|
||||
if (entity_type == EntityType::Relationship)
|
||||
action_data.actions[entity] = ClauseAction::UpdateRelationship;
|
||||
|
||||
action_data.parameter_index.emplace(ParameterIndexKey(entity, prop), index);
|
||||
action_data.parameter_index.emplace(ParameterIndexKey(entity, prop),
|
||||
index);
|
||||
action_data.add_entitiy_property(entity, prop);
|
||||
|
||||
clear_state();
|
||||
@ -397,14 +439,43 @@ public:
|
||||
void visit(ast::Accessor &ast_accessor) override
|
||||
{
|
||||
if (!ast_accessor.has_entity()) return;
|
||||
|
||||
auto &action_data = generator.action_data();
|
||||
|
||||
if (state == CypherState::Return) {
|
||||
auto &return_elements = action_data.return_elements;
|
||||
auto &entity = ast_accessor.entity_name();
|
||||
if (!ast_accessor.has_prop()) {
|
||||
return_elements.emplace_back(ReturnElement(entity));
|
||||
} else {
|
||||
auto &property = ast_accessor.entity_prop();
|
||||
return_elements.emplace_back(ReturnElement(entity, property));
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_accessor.has_prop()) return;
|
||||
|
||||
set_element_state.set_entity = ast_accessor.entity_name();
|
||||
set_element_state.set_prop = ast_accessor.entity_prop();
|
||||
if (state == CypherState::Set) {
|
||||
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);
|
||||
}
|
||||
|
||||
void visit(ast::Delete& ast_delete) override
|
||||
{
|
||||
code += generator.generate();
|
||||
|
||||
state = CypherState::Delete;
|
||||
|
||||
generator.add_action(QueryAction::Delete);
|
||||
|
||||
Traverser::visit(ast_delete);
|
||||
|
||||
code += generator.generate();
|
||||
}
|
||||
};
|
||||
|
@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: wrap fmt format
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "code.hpp"
|
||||
#include "cypher/visitor/traverser.hpp"
|
||||
#include "query_engine/util.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
#include "query_engine/code_generator/entity_search.hpp"
|
||||
|
||||
using namespace entity_search;
|
||||
|
||||
class ReadTraverser : public Traverser, public Code
|
||||
{
|
||||
private:
|
||||
// TODO: change int to something bigger (int64_t)
|
||||
std::map<std::string, int> internal_ids;
|
||||
|
||||
std::set<std::string> entities;
|
||||
CypherStateMachine csm;
|
||||
uint32_t index{0};
|
||||
std::map<std::string, uint32_t> property_indices;
|
||||
|
||||
public:
|
||||
void update_entities(const std::string &name)
|
||||
{
|
||||
std::cout << name << std::endl;
|
||||
if (entities.find(name) == entities.end()) {
|
||||
csm.init_cost(name);
|
||||
property_indices[name] = index++;
|
||||
entities.insert(std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void visit(ast::Match &match) override
|
||||
{
|
||||
code += LINE(code::transaction_begin);
|
||||
Traverser::visit(match);
|
||||
};
|
||||
|
||||
void visit(ast::Node &node) override
|
||||
{
|
||||
auto identifier = node.idn;
|
||||
if (identifier == nullptr) return;
|
||||
|
||||
auto name = identifier->name;
|
||||
update_entities(name);
|
||||
|
||||
if (node.labels != nullptr && node.labels->value != nullptr)
|
||||
csm.search_cost(name, search_label_index, label_cost);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
update_entities(name);
|
||||
csm.search_cost(name, search_internal_id, internal_id_cost);
|
||||
}
|
||||
|
||||
void visit(ast::Return &ret) override
|
||||
{
|
||||
for (auto const &entity : entities) {
|
||||
std::cout << entity << std::endl;
|
||||
std::cout << underlying_cast(csm.min(entity)) << std::endl;
|
||||
if (csm.min(entity) == search_internal_id) {
|
||||
auto property_index = property_indices.at(entity);
|
||||
code += LINE(
|
||||
fmt::format(code::args_id, std::to_string(property_index)));
|
||||
code += LINE(code::vertex_accessor_args_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (csm.min(entity) == search_label_index) {
|
||||
code += LINE(fmt::format(code::todo, "search label index"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (csm.min(entity) == search_main_storage) {
|
||||
code += LINE(fmt::format(code::todo, "return all vertices"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
code::debug_print_vertex_labels();
|
||||
|
||||
code += LINE(code::transaction_commit);
|
||||
code += LINE(code::return_empty_result);
|
||||
}
|
||||
};
|
@ -1,270 +0,0 @@
|
||||
#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,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <typeinfo>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "cypher/visitor/traverser.hpp"
|
||||
#include "query_engine/util.hpp"
|
||||
|
||||
class WriteTraverser : public Traverser
|
||||
{
|
||||
private:
|
||||
uint32_t index{0};
|
||||
std::vector<std::string> labels;
|
||||
bool collecting_labels{false};
|
||||
|
||||
public:
|
||||
std::string code;
|
||||
|
||||
void visit(ast::Create &create) override
|
||||
{
|
||||
code += LINE(code::transaction_begin);
|
||||
code += LINE(code::vertex_accessor);
|
||||
|
||||
Traverser::visit(create);
|
||||
};
|
||||
|
||||
void visit(ast::LabelList &label_list) override
|
||||
{
|
||||
// TODO: dummy approach -> discussion
|
||||
if (!collecting_labels) collecting_labels = true;
|
||||
|
||||
Traverser::visit(label_list);
|
||||
|
||||
if (collecting_labels) {
|
||||
for (auto label : labels) {
|
||||
code += LINE(fmt::format(code::create_label, label, label));
|
||||
code += LINE(fmt::format(code::add_label, label));
|
||||
}
|
||||
labels.clear();
|
||||
collecting_labels = false;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(ast::Identifier &identifier) override
|
||||
{
|
||||
if (collecting_labels) labels.push_back(identifier.name);
|
||||
}
|
||||
|
||||
void visit(ast::Property &property) override
|
||||
{
|
||||
auto key = property.idn->name;
|
||||
code += LINE(fmt::format(code::vertex_set_property, key, index));
|
||||
++index;
|
||||
}
|
||||
|
||||
void visit(ast::Return &ret) override
|
||||
{
|
||||
code += LINE(code::transaction_commit);
|
||||
code += LINE(code::return_empty_result);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user