Query engine work in progress: code_generator handlers, update clause generator, delete clause generator, return clause generator, SemanticError

This commit is contained in:
Marko Budiselic 2016-07-25 02:09:40 +01:00
parent 356e9444ec
commit 6970170f69
22 changed files with 360 additions and 563 deletions

View File

@ -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; }
};
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View 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"

View 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;
};

View 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;
};

View 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;

View 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;
};

View 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;
};

View 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;
};

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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);
}

View 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) {}
};

View File

@ -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;

View File

@ -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: {}";

View File

@ -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();
}
};

View File

@ -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);
}
};

View File

@ -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++);
}
}
};

View File

@ -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);
}
};