Basic CREATE query works (e.g. CREATE (n {age:25, name:"test"}) RETURN n)

This commit is contained in:
Marko Budiselic 2016-02-21 22:21:15 +01:00
parent 99a0fcd8e3
commit d77800b2f9
25 changed files with 302 additions and 178 deletions

View File

@ -3,6 +3,11 @@
#include <string>
#include <stdexcept>
#include <dlfcn.h>
#include <atomic>
#include <iostream>
using std::cout;
using std::endl;
template<typename T>
class DynamicLib
@ -10,26 +15,25 @@ class DynamicLib
private:
using produce_t = typename T::produce;
using destruct_t = typename T::destruct;
std::atomic<uint8_t> counter;
public:
produce_t produce_method;
destruct_t destruct_method;
DynamicLib(const std::string& lib_path) :
lib_path(lib_path)
lib_path(lib_path),
lib_object(nullptr)
{
load();
}
// TODO debug why this doesn't work
typename T::lib_object* instance()
{
// TODO singleton, lazy, concurrency
// ifs are uncommented -> SEGMENTATION FAULT
// TODO debug
// if (dynamic_lib == nullptr)
this->load();
// if (dynamic_lib == nullptr)
// TODO singleton, concurrency
if (lib_object == nullptr) {
lib_object = this->produce_method();
}
return lib_object;
}
@ -50,7 +54,7 @@ public:
private:
std::string lib_path;
void *dynamic_lib;
typename T::lib_object* lib_object;
typename T::lib_object *lib_object;
void load_lib()
{

View File

@ -7,15 +7,17 @@
class CodeCompiler
{
public:
void compile(const std::string& in_file, const std::string& out_file)
{
auto compile_command = utils::prints(
"clang++",
"-std=c++1y",
in_file,
"-o", out_file,
"-I../",
"-shared -fPIC"
// "-std=c++1y -O2 -DNDEBUG", // compile flags
"-std=c++1y", // compile flags
in_file, // input file
"-o", out_file, // ouput file
"-I../", // include paths
"-shared -fPIC" // shared library flags
);
// synchronous call

View File

@ -1,27 +0,0 @@
#pragma once
#include <string>
#include "i_code_cpu.hpp"
#include "database/db.hpp"
#include "utils/log/logger.hpp"
// preparations before execution
// execution
// postprocess the results
using std::cout;
using std::endl;
class CodeExecutor
{
public:
void execute(ICodeCPU *code_cpu)
{
auto result = code_cpu->run(db);
cout << result->result << endl;
}
private:
Db db;
};

View File

@ -10,19 +10,6 @@ using std::string;
class CodeGenerator
{
public:
void generate_hpp(const uint64_t stripped_hash, const std::string& path)
{
string template_path = CONFIG(config::TEMPLATE_CPU_HPP_PATH);
string template_file = utils::read_file(template_path.c_str());
string generated = template_engine.render(
template_file,
{
{"stripped_hash", std::to_string(stripped_hash)},
{"data_type", "int"}
}
);
utils::write_file(generated, path);
}
void generate_cpp(const std::string& query,
const uint64_t stripped_hash,

View File

@ -5,4 +5,5 @@
# clang++ -std=c++1y create_return.cpp -o create_return.so -I../../../ -shared -fPIC
# cd ../..
# clang++ -std=c++1y -g -I../ -I ../lib/yaml-cpp/include main.cpp ../cypher/cypher.cpp -o engine -L ../lib/yaml-cpp/build -l yaml-cpp -l dl
# clang++ -std=c++1y -g -O2 -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread
clang++ -std=c++1y -g -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread

23
query_engine/debug.hpp Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <iostream>
#include "storage/model/properties/traversers/jsonwriter.hpp"
#include "storage/model/properties/properties.hpp"
using std::cout;
using std::endl;
void print_props(const Properties& properties)
{
StringBuffer buffer;
JsonWriter<StringBuffer> writer(buffer);
properties.accept(writer);
cout << buffer.str() << endl;
}
#ifdef DEBUG
# define PRINT_PROPS(_PROPS_) print_props(_PROPS_);
#else
# define PRINT_PROPS(_)
#endif

View File

@ -1,12 +1,13 @@
#pragma once
#include "query_engine/query_result.hpp"
#include "query_stripped.hpp"
#include "database/db.hpp"
class ICodeCPU
{
public:
virtual QueryResult::sptr run(Db& db) = 0;
virtual QueryResult::sptr run(Db& db, code_args_t& args) = 0;
virtual ~ICodeCPU() {}
};

View File

@ -3,6 +3,7 @@
#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;
@ -17,7 +18,18 @@ int main(int argc, char** argv)
cout << "QUERY: " << cypher_query << endl;
QueryEngine engine;
engine.execute(cypher_query);
// 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;
return 0;
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <string>
#include "query_program.hpp"
#include "database/db.hpp"
#include "utils/log/logger.hpp"
// preparations before execution
// execution
// postprocess the results
#define DEBUG 1
#include "query_engine/debug.hpp"
using std::cout;
using std::endl;
class ProgramExecutor
{
public:
auto execute(QueryProgram& program)
{
auto result = program.code->run(db, program.stripped.arguments);
print_props(*result->data["n"]->data[0]);
return result;
}
public:
Db db;
};

View File

@ -4,7 +4,7 @@
#include <unordered_map>
#include <memory>
// #define NOT_LOG_INFO
#define NOT_LOG_INFO
#include "memgraph_dynamic_lib.hpp"
#include "query_stripper.hpp"
@ -13,28 +13,27 @@
#include "utils/hashing/fnv.hpp"
#include "config/config.hpp"
#include "utils/log/logger.hpp"
#include "query_program.hpp"
using std::string;
using std::cout;
using std::endl;
class CodeLoader
class ProgramLoader
{
public:
using sptr_code_lib = std::shared_ptr<CodeLib>;
CodeLoader()
: stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR))
{
}
ProgramLoader()
: stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL)) {}
ICodeCPU* load_code_cpu(const string& query)
auto load(const string& query)
{
auto stripped = stripper.strip(query);
LOG_INFO("stripped_query=" + stripped);
auto stripped_hash = fnv(stripped);
LOG_INFO("stripped_query=" + stripped.query);
auto stripped_hash = fnv(stripped.query);
auto hash_string = std::to_string(stripped_hash);
LOG_INFO("query_hash=" + hash_string);
@ -42,9 +41,10 @@ public:
// code is already compiled and loaded, just return runnable
// instance
if (code_lib_iter != code_libs.end())
// TODO also return extracted arguments
return code_lib_iter->second->instance();
if (code_lib_iter != code_libs.end()) {
auto code = code_lib_iter->second->instance();
return QueryProgram(code, std::move(stripped));
}
// code has to be generated, compiled and loaded
// TODO load output path from config
@ -61,12 +61,13 @@ public:
code_libs.insert({{stripped_hash, code_lib}});
// return instance of runnable code (ICodeCPU)
return code_lib->instance();
return QueryProgram(code_lib->instance(), std::move(stripped));
}
private:
// TODO somehow remove int.. from here
QueryStripper<int, int, int> stripper;
QueryStripper<int, int, int, int> stripper;
// TODO ifdef MEMGRAPH64 problem, how to use this kind
// of ifdef functions?
// uint64_t depends on fnv function

View File

@ -1,30 +1,26 @@
#pragma once
#include "code_loader.hpp"
#include "code_executor.hpp"
#include "program_loader.hpp"
#include "program_executor.hpp"
#include "query_result.hpp"
//
// Current arhitecture:
// query -> code_loader -> query_stripper -> [code_generator]
// -> [code_compiler] -> code_executor
//
class QueryEngine
{
public:
QueryEngine()
{
}
QueryResult* execute(const std::string& query)
auto execute(const std::string& query)
{
executor.execute(loader.load_code_cpu(query));
throw std::runtime_error("implement me");
auto program = program_loader.load(query);
auto result = program_executor.execute(program);
return result;
}
private:
CodeLoader loader;
CodeExecutor executor;
ProgramExecutor program_executor;
ProgramLoader program_loader;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include "i_code_cpu.hpp"
#include "query_stripped.hpp"
struct QueryProgram
{
QueryProgram(ICodeCPU* code, QueryStripped&& stripped) :
code(code),
stripped(std::forward<QueryStripped>(stripped)) {}
QueryProgram(QueryProgram& other) = delete;
QueryProgram(QueryProgram&& other) = default;
ICodeCPU *code;
QueryStripped stripped;
};

View File

@ -1,15 +1,38 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
class QueryResult
#include "storage/model/properties/properties.hpp"
struct ResultList
{
public:
using sptr = std::shared_ptr<QueryResult>;
using sptr = std::shared_ptr<ResultList>;
using data_t = std::vector<const Properties*>;
QueryResult(std::string result) :
result(result) {}
ResultList() = delete;
ResultList(ResultList& other) = delete;
ResultList(ResultList&& other) = default;
std::string result;
ResultList(data_t&& data) :
data(std::forward<data_t>(data)) {}
std::vector<const Properties*> data;
};
struct QueryResult
{
using sptr = std::shared_ptr<QueryResult>;
using data_t = std::unordered_map<std::string, ResultList::sptr>;
QueryResult() = delete;
QueryResult(QueryResult& other) = delete;
QueryResult(QueryResult&& other) = default;
QueryResult(data_t&& data) :
data(std::forward<data_t>(data)) {}
data_t data;
};

View File

@ -0,0 +1,20 @@
#pragma once
#include <vector>
#include "storage/model/properties/property.hpp"
using code_args_t = std::vector<Property::sptr>;
struct QueryStripped
{
QueryStripped(const std::string&& query, code_args_t&& arguments) :
query(std::forward<const std::string>(query)),
arguments(std::forward<code_args_t>(arguments)) {}
QueryStripped(QueryStripped& other) = delete;
QueryStripped(QueryStripped&& other) = default;
std::string query;
code_args_t arguments;
};

View File

@ -3,13 +3,22 @@
#include <string>
#include <tuple>
#include <utility>
#include <unordered_map>
#include "cypher/cypher.h"
#include "cypher/tokenizer/cypher_lexer.hpp"
#include "utils/variadic/variadic.hpp"
#include "storage/model/properties/all.hpp"
#include "query_stripped.hpp"
#include <iostream>
template<class T, class V>
void store_query_param(code_args_t& arguments, V&& v)
{
arguments.emplace_back(std::make_shared<T>(std::forward<V>(v)));
}
template<typename ...Ts>
class QueryStripper
{
@ -17,32 +26,63 @@ public:
QueryStripper(Ts&&... strip_types) :
lexer(std::make_unique<CypherLexer>()),
strip_types(std::make_tuple(std::forward<Ts>(strip_types)...))
{
}
strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)) {}
QueryStripper(QueryStripper& other) = delete;
QueryStripper(QueryStripper&& other) :
strip_types(std::move(other.strip_types)),
lexer(std::move(other.lexer))
{
}
lexer(std::move(other.lexer)) {}
decltype(auto) strip(const std::string& query)
auto strip(const std::string& query)
{
// TODO return hash and arguments
// TODO write this more optimal (resplace string
// concatenation with something smarter)
// TODO: in place substring replacement
auto tokenizer = lexer->tokenize(query);
std::string stripped = "";
int counter = 0;
// TMP size of supported token types
constexpr auto size = std::tuple_size<decltype(strip_types)>::value;
int counter = 0;
code_args_t stripped_arguments;
std::string stripped_query;
stripped_query.reserve(query.size());
while (auto token = tokenizer.lookup())
{
// TODO: better implementation
if (_or(token.id, strip_types, std::make_index_sequence<size>{})) {
stripped += "@" + std::to_string(counter++);
auto index = counter++;
switch (token.id) {
case TK_INT:
store_query_param<Int32>(stripped_arguments,
std::stoi(token.value));
break;
case TK_STR:
store_query_param<String>(stripped_arguments,
token.value);
break;
case TK_BOOL: {
bool value = token.value[0] == 'T' ||
token.value[0] == 't';
store_query_param<Bool>(stripped_arguments, value);
break;
}
case TK_FLOAT:
store_query_param<Float>(stripped_arguments,
std::stof(token.value));
break;
}
stripped_query += std::to_string(index);
} else {
stripped += token.value;
stripped_query += token.value;
}
}
return stripped;
return QueryStripped(std::move(stripped_query),
std::move(stripped_arguments));
}
private:

View File

@ -2,6 +2,7 @@
#include <string>
#include "query_engine/i_code_cpu.hpp"
#include "storage/model/properties/all.hpp"
// TODO generate with the template engine
// #include "storage/model/properties/jsonwriter.hpp"
@ -15,7 +16,7 @@ class {{class_name}} : public ICodeCPU
{
public:
QueryResult::sptr run(Db& db) override
QueryResult::sptr run(Db& db, code_args_t& args) override
{
{{code}} }

View File

@ -1,9 +0,0 @@
#pragma once
#include "query_engine/i_code_cpu.hpp"
class ICodeCPUData : ICodeCPU
{
public:
{{data_type}} data;
};

View File

@ -4,38 +4,52 @@
#include <typeinfo>
#include <map>
#include "storage/model/properties/properties.hpp"
#include "storage/model/properties/jsonwriter.hpp"
#include "cypher/visitor/traverser.hpp"
#include "node_traverser.hpp"
#include "query_engine/util.hpp"
using std::cout;
using std::endl;
class CreateTraverser : public Traverser
{
private:
std::string key;
uint32_t index{0};
public:
std::string code;
Properties properties;
void visit(ast::Create& create) override
{
code = "\t\tauto& t = db.tx_engine.begin();\n";
code += "\t\tauto vertex_accessor = db.graph.vertices.insert(t);\n";
code += line("auto& t = db.tx_engine.begin();");
code += line("auto vertex_accessor = db.graph.vertices.insert(t);");
Traverser::visit(create);
};
void visit(ast::Node& node) override
void visit(ast::Property& property) override
{
Traverser::visit(node);
key = property.idn->name;
Traverser::visit(property);
code += line("vertex_accessor.property(");
code += line("\t\"" + key + "\", args[" + std::to_string(index) + "]");
code += line(");");
++index;
}
void visit(ast::Return& ret) override
{
code += "\t\tauto properties = vertex_accessor.properties();\n";
code += "\t\tStringBuffer buffer;\n";
code += "\t\tJsonWriter<StringBuffer> writer(buffer);\n";
code += "\t\tproperties.accept(writer);\n";
code += "\t\treturn std::make_shared<QueryResult>(buffer.str());\n";
code += line("t.commit();");
code += line("auto &properties = vertex_accessor.properties();");
code += line("ResultList::data_t data = {&properties};");
code += line("auto result_data = "
"std::make_shared<ResultList>(std::move(data));");
code += line("QueryResult::data_t query_data = {{\"" +
ret.return_list->value->name + "\", result_data}};");
code += line("return std::make_shared<QueryResult>"
"(std::move(query_data));");
}
};

View File

@ -1,49 +0,0 @@
#pragma once
#include "cypher/visitor/traverser.hpp"
#include "storage/model/properties/properties.hpp"
struct NodeTraverser : public Traverser
{
Properties traverse(ast::Node& node)
{
Traverser::visit(node);
return properties;
}
void visit(ast::Property& property) override
{
key = property.idn->name;
Traverser::visit(property);
if (flag == Property::Flags::Int32) {
properties.set<Int32>(key, *int_value);
}
if (flag == Property::Flags::String) {
properties.set<String>(key, *string_value);
}
}
void visit(ast::String& string) override
{
flag = Property::Flags::String;
string_value = std::make_shared<String>(string.value);
}
void visit(ast::Integer& integer) override
{
flag = Property::Flags::Int32;
int_value = std::make_shared<Int32>(integer.value);
}
void visit(ast::Node& node) override
{
Traverser::visit(node);
}
private:
std::string key;
Property::Flags flag;
std::shared_ptr<Int32> int_value;
std::shared_ptr<String> string_value;
Properties properties;
};

8
query_engine/util.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
std::string line(std::string line)
{
return "\t\t" + line + "\n";
}

View File

@ -2,7 +2,7 @@
#include "mvcc/record.hpp"
#include "model/edge_model.hpp"
#include "model/properties/jsonwriter.hpp"
#include "model/properties/traversers/jsonwriter.hpp"
class Edge : public mvcc::Record<Edge>
{

View File

@ -7,9 +7,9 @@
class Properties
{
using props_t = std::map<std::string, Property::sptr>;
public:
using sptr = std::shared_ptr<Properties>;
const Property& at(const std::string& key) const
{
auto it = props.find(key);
@ -56,6 +56,8 @@ public:
}
private:
using props_t = std::map<std::string, Property::sptr>;
props_t props;
};

View File

@ -60,6 +60,11 @@ public:
record->data.props.template set<V>(key, std::forward<Args>(args)...);
}
void property(const std::string& key, Property::sptr value)
{
record->data.props.set(key, std::move(value));
}
Properties& properties() const
{
return record->data.props;

View File

@ -2,7 +2,7 @@
#include "mvcc/record.hpp"
#include "model/vertex_model.hpp"
#include "model/properties/jsonwriter.hpp"
#include "model/properties/traversers/jsonwriter.hpp"
class Vertex : public mvcc::Record<Vertex>
{

19
utils/time/timer.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <utility>
#include <chrono>
using time_point_t = std::chrono::high_resolution_clock::time_point;
#define duration(a) \
std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define time_now() std::chrono::high_resolution_clock::now()
template<typename F, typename... Args>
double timer(F func, Args&&... args)
{
time_point_t start_time = time_now();
func(std::forward<Args>(args)...);
return duration(time_now() - start_time);
}