antlr integration, *.hpp and *.cpp inside src dir, cleanup
Summary: antlr integration, *.hpp and *.cpp inside src dir, cleanup Test Plan: manual Reviewers: mislav.bradac, dgleich, florijan Reviewed By: florijan Subscribers: pullbot, buda Differential Revision: https://phabricator.memgraph.io/D49
This commit is contained in:
parent
95dcb4c1ba
commit
e7f5bd4c21
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ build/compiled/
|
||||
cmake-build-*
|
||||
.idea
|
||||
cmake/DownloadProject/
|
||||
dist/
|
||||
|
183
CMakeLists.txt
183
CMakeLists.txt
@ -5,12 +5,14 @@ cmake_minimum_required(VERSION 3.1)
|
||||
# !! IMPORTANT !! run ./project_root/init.sh before cmake command
|
||||
# to download dependencies
|
||||
|
||||
if(NOT UNIX)
|
||||
message(FATAL "Unsupported operating system.")
|
||||
endif()
|
||||
|
||||
# choose a compiler
|
||||
# NOTE: must be choosen before use of project() or enable_language()
|
||||
if (UNIX)
|
||||
# NOTE: must be choosen before use of project() or enable_language() ----------
|
||||
set(CMAKE_C_COMPILER "clang")
|
||||
set(CMAKE_CXX_COMPILER "clang++")
|
||||
endif (UNIX)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# set project name
|
||||
@ -50,11 +52,28 @@ set(test_include_dir ${CMAKE_BINARY_DIR}/tests/include)
|
||||
set(test_src_dir ${CMAKE_BINARY_DIR}/tests/src)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# setup external dependencies
|
||||
# lemon & lempar
|
||||
set(lemon_dir ${libs_dir}/lemon)
|
||||
# lexertl
|
||||
set(lexertl_dir ${libs_dir}/lexertl)
|
||||
# build flags -----------------------------------------------------------------
|
||||
# release flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
#debug flags
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
|
||||
# compiler specific flags
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "-Wl,--export-dynamic ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "-rdynamic ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
endif()
|
||||
|
||||
# default build type is debug
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
set(CMAKE_BUILD_TYPE "debug")
|
||||
endif()
|
||||
message(STATUS "CMake build type: ${CMAKE_BUILD_TYPE}")
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# setup external dependencies -------------------------------------------------
|
||||
add_subdirectory(libs)
|
||||
# fmt
|
||||
set(fmt_source_dir ${libs_dir}/fmt)
|
||||
set(fmt_static_lib ${fmt_source_dir}/fmt/libfmt.a)
|
||||
@ -62,31 +81,8 @@ set(fmt_static_lib ${fmt_source_dir}/fmt/libfmt.a)
|
||||
set(yaml_source_dir ${libs_dir}/yaml-cpp)
|
||||
set(yaml_include_dir ${yaml_source_dir}/include)
|
||||
set(yaml_static_lib ${yaml_source_dir}/libyaml-cpp.a)
|
||||
# Catch (C++ Automated Test Cases in Headers)
|
||||
set(catch_source_dir "${libs_dir}/Catch")
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# load cmake modules: cmake/*.cmake
|
||||
include(gtest)
|
||||
include(gbenchmark)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# build memgraph's cypher grammar
|
||||
# copy grammar file to the build directory
|
||||
FILE(COPY ${include_dir}/query/language/cypher/cypher.y
|
||||
DESTINATION ${CMAKE_BINARY_DIR})
|
||||
# build cypher parser (only c file - cypher.c)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${lemon_dir}/lemon ${CMAKE_BINARY_DIR}/cypher.y -s
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
# change cypher parser c extension to cpp (cypher.c -> cypher.cpp)
|
||||
FILE(RENAME ${CMAKE_BINARY_DIR}/cypher.c ${CMAKE_BINARY_DIR}/cypher.cpp)
|
||||
# add include file (cypher.h) to build include dir
|
||||
SET(cypher_build_include_dir ${build_include_dir}/cypher)
|
||||
FILE(MAKE_DIRECTORY ${cypher_build_include_dir})
|
||||
FILE(RENAME ${CMAKE_BINARY_DIR}/cypher.h ${cypher_build_include_dir}/cypher.h)
|
||||
# -----------------------------------------------------------------------------
|
||||
# antlr
|
||||
set(antlr_static_lib ${CMAKE_SOURCE_DIR}/dist/libantlr4-runtime.a)
|
||||
|
||||
# prepare template and destination folders for query engine (tests)
|
||||
# and memgraph server binary
|
||||
@ -111,7 +107,8 @@ FILE(COPY ${tests_dir}/integration/stream
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# copy files needed for query engine (headers)
|
||||
include(copy_includes)
|
||||
# TODO: copy includes for runtime compilation
|
||||
# include(copy_includes)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# linter setup (clang-tidy)
|
||||
@ -137,27 +134,7 @@ if(CLANG_TIDY)
|
||||
endif()
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# TODO: add specific flags
|
||||
# release flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
#debug flags
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
|
||||
# compiler specific flags
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "-Wl,--export-dynamic ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "-rdynamic ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
endif()
|
||||
|
||||
# default build type is debug
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
set(CMAKE_BUILD_TYPE "debug")
|
||||
endif()
|
||||
message(STATUS "CMake build type: ${CMAKE_BUILD_TYPE}")
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# logging levels
|
||||
# logging levels --------------------------------------------------------------
|
||||
option(LOG_NO_TRACE "Disable trace logging" OFF)
|
||||
message(STATUS "LOG_NO_TRACE: ${LOG_NO_TRACE}")
|
||||
if (LOG_NO_TRACE)
|
||||
@ -187,7 +164,6 @@ message(STATUS "LOG_NO_ERROR: ${LOG_NO_ERROR}")
|
||||
if (LOG_NO_ERROR)
|
||||
add_definitions(-DLOG_NO_ERROR)
|
||||
endif()
|
||||
# TODO: find a way how to applay those defines at the query compile time
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# logger type
|
||||
@ -269,24 +245,54 @@ include_directories(${src_dir})
|
||||
include_directories(${build_include_dir})
|
||||
include_directories(${fmt_source_dir})
|
||||
include_directories(${yaml_include_dir})
|
||||
include_directories(${http_parser_source_dir})
|
||||
include_directories(${lexertl_dir})
|
||||
include_directories(${libuv_source_dir}/include)
|
||||
include_directories(${rapidjson_source_dir}/include)
|
||||
include_directories(${r3_source_dir}/include)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# creates build/libcypher_lib.a
|
||||
add_library(cypher_lib STATIC ${CMAKE_BINARY_DIR}/cypher.cpp)
|
||||
# -----------------------------------------------------------------------------
|
||||
# openCypher parser -----------------------------------------------------------
|
||||
set(antlr_src ${CMAKE_SOURCE_DIR}/libs/antlr4/runtime/Cpp/runtime/src)
|
||||
set(opencypher_frontend ${CMAKE_SOURCE_DIR}/src/query/frontend/opencypher)
|
||||
set(opencypher_generated ${opencypher_frontend}/generated)
|
||||
set(opencypher_grammar ${opencypher_frontend}/grammar/Cypher.g4)
|
||||
|
||||
# TODO: remove from here (isolate HTTP server)
|
||||
# # REST API preprocessor
|
||||
# EXECUTE_PROCESS(
|
||||
# COMMAND python link_resources.py
|
||||
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/api
|
||||
# )
|
||||
# # ---------------------------------------------------------------------------
|
||||
# enumerate all files that are generated from antlr
|
||||
set(antlr_opencypher_generated_src
|
||||
${opencypher_frontend}/generated/CypherLexer.cpp
|
||||
${opencypher_frontend}/generated/CypherParser.cpp
|
||||
${opencypher_frontend}/generated/CypherBaseVisitor.cpp
|
||||
${opencypher_frontend}/generated/CypherVisitor.cpp
|
||||
)
|
||||
|
||||
# set generated flag to true
|
||||
# foreach(src_file ${antlr_opencypher_generated_src})
|
||||
# set_source_files_properties(
|
||||
# ${src_file}
|
||||
# PROPERTIES
|
||||
# GENERATED TRUE
|
||||
# )
|
||||
# endforeach(src_file ${antlr_opencypher_generated_src})
|
||||
|
||||
# add custom target for generation
|
||||
add_custom_target(generate_opencypher_parser
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
||||
COMMAND
|
||||
java -jar ${CMAKE_BINARY_DIR}/antlr-4.6-complete.jar -Dlanguage=Cpp -visitor -o ${opencypher_generated} -package antlrcpptest ${opencypher_grammar}
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
DEPENDS ${opencypher_grammar}
|
||||
)
|
||||
|
||||
# include antlr header files
|
||||
include_directories(
|
||||
${antlr_src}
|
||||
${antlr_src}/misc
|
||||
${antlr_src}/atn
|
||||
${antlr_src}/dfa
|
||||
${antlr_src}/tree
|
||||
${antlr_src}/support
|
||||
)
|
||||
|
||||
add_library(antlr_opencypher_parser_lib STATIC ${antlr_opencypher_generated_src})
|
||||
target_link_libraries(antlr_opencypher_parser_lib ${antlr_static_lib})
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# all memgraph src files
|
||||
set(memgraph_src_files
|
||||
@ -316,10 +322,10 @@ set(memgraph_src_files
|
||||
${src_dir}/storage/typed_value.cpp
|
||||
${src_dir}/storage/locking/record_lock.cpp
|
||||
# ${src_dir}/storage/garbage/garbage.cpp
|
||||
${src_dir}/storage/record_accessor.cpp
|
||||
${src_dir}/storage/record_accessor.cpp
|
||||
${src_dir}/storage/vertex_accessor.cpp
|
||||
${src_dir}/storage/edge_accessor.cpp
|
||||
# ${src_dir}/storage/record_accessor.cpp
|
||||
${src_dir}/storage/edge_accessor.cpp
|
||||
# ${src_dir}/storage/record_accessor.cpp
|
||||
${src_dir}/transactions/snapshot.cpp
|
||||
${src_dir}/transactions/transaction.cpp
|
||||
${src_dir}/transactions/transaction_read.cpp
|
||||
@ -357,6 +363,11 @@ if (ALL_TESTS OR BENCHMARK_TESTS OR CONCURRENT_TEST OR INTEGRATION_TEST
|
||||
endif()
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
execute_process(
|
||||
COMMAND python recursive_include.py --root ${src_dir} --start ${src_dir}/query/plan_interface.hpp --copy ${CMAKE_BINARY_DIR}/include
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cmake
|
||||
)
|
||||
|
||||
# memgraph build name
|
||||
execute_process(
|
||||
OUTPUT_VARIABLE COMMIT_BRANCH
|
||||
@ -384,14 +395,20 @@ if (MEMGRAPH)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} memgraph_lib)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} stdc++fs)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} Threads::Threads)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} cypher_lib)
|
||||
|
||||
if (UNIX)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} crypto)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} ssl)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${fmt_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${yaml_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} dl)
|
||||
endif (UNIX)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} crypto)
|
||||
# target_link_libraries(${MEMGRAPH_BUILD_NAME} ssl)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${fmt_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${yaml_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} ${antlr_static_lib})
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} antlr_opencypher_parser_lib)
|
||||
target_link_libraries(${MEMGRAPH_BUILD_NAME} dl)
|
||||
endif()
|
||||
# add_dependencies(${MEMGRAPH_BUILD_NAME} generate_opencypher_parser)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# make CLion aware of all source files so we get refactoring etc
|
||||
# TODO: fix snapshots and everything from src and then add custom target
|
||||
#file(GLOB_RECURSE __SOURCES ${CMAKE_SOURCE_DIR}/src/*.hpp ${CMAKE_SOURCE_DIR}/src/*.cpp)
|
||||
#add_executable(__refactor_target ${__SOURCES})
|
||||
#target_link_libraries(__refactor_target memgraph_lib stdc++fs Threads::Threads ${fmt_static_lib} ${yaml_static_lib} ${antlr_static_lib} antlr_opencypher_parser_lib dl)
|
||||
#add_dependencies(__refactor_target generate_opencypher_parser)
|
||||
|
1
Doxyfile
1
Doxyfile
@ -837,6 +837,7 @@ EXCLUDE_PATTERNS += */libs/*
|
||||
EXCLUDE_PATTERNS += */release/*
|
||||
EXCLUDE_PATTERNS += */Testing/*
|
||||
EXCLUDE_PATTERNS += */tests/*
|
||||
EXCLUDE_PATTERNS += */dist/*
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
|
@ -1,139 +0,0 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# COPY header files required by query engine (query compiler)
|
||||
# TODO: create a copy function, COPY_RELATIVE(base relative_path dst)
|
||||
# TODO: somehow automate (in destination dir should be only required include files)
|
||||
FILE(COPY ${include_dir}/database/graph_db.hpp DESTINATION ${build_include_dir}/database)
|
||||
|
||||
FILE(COPY ${include_dir}/storage/edge.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/edge_accessor.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/vertex.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/vertex_accessor.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/record_accessor.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/locking/record_lock.hpp DESTINATION ${build_include_dir}/storage/locking)
|
||||
FILE(COPY ${include_dir}/storage/locking/lock_status.hpp DESTINATION ${build_include_dir}/storage/locking)
|
||||
|
||||
FILE(COPY ${include_dir}/query/util.hpp DESTINATION ${build_include_dir}/query)
|
||||
FILE(COPY ${include_dir}/query/plan_interface.hpp DESTINATION ${build_include_dir}/query)
|
||||
FILE(COPY ${include_dir}/query/stripper.hpp DESTINATION ${build_include_dir}/query)
|
||||
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/concurrent_map.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/concurrent_set.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/concurrent_list.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/common.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/skiplist.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/concurrent/skiplist_gc.hpp DESTINATION ${build_include_dir}/data_structures/concurrent)
|
||||
FILE(COPY ${include_dir}/data_structures/map/rh_hashmultimap.hpp DESTINATION ${build_include_dir}/data_structures/map)
|
||||
FILE(COPY ${include_dir}/data_structures/map/rh_common.hpp DESTINATION ${build_include_dir}/data_structures/map)
|
||||
|
||||
FILE(COPY ${include_dir}/data_structures/bitset/dynamic_bitset.hpp DESTINATION ${build_include_dir}/data_structures/bitset)
|
||||
|
||||
FILE(COPY ${include_dir}/threading/sync/lockable.hpp DESTINATION ${build_include_dir}/threading/sync)
|
||||
FILE(COPY ${include_dir}/threading/sync/spinlock.hpp DESTINATION ${build_include_dir}/threading/sync)
|
||||
FILE(COPY ${include_dir}/threading/sync/futex.hpp DESTINATION ${build_include_dir}/threading/sync)
|
||||
FILE(COPY ${include_dir}/threading/sync/lock_timeout_error.hpp DESTINATION ${build_include_dir}/threading/sync)
|
||||
|
||||
FILE(COPY ${include_dir}/memory/freelist.hpp DESTINATION ${build_include_dir}/memory)
|
||||
FILE(COPY ${include_dir}/memory/lazy_gc.hpp DESTINATION ${build_include_dir}/memory)
|
||||
|
||||
FILE(COPY ${include_dir}/mvcc/cre_exp.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/hints.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/id.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/mvcc_error.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/record.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/serialization_error.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/version.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
FILE(COPY ${include_dir}/mvcc/version_list.hpp DESTINATION ${build_include_dir}/mvcc)
|
||||
|
||||
FILE(COPY ${include_dir}/transactions/transaction.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/lock_store.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/snapshot.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/commit_log.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/engine.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/transaction_store.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
FILE(COPY ${include_dir}/transactions/transaction_read.hpp DESTINATION ${build_include_dir}/transactions)
|
||||
|
||||
FILE(COPY ${include_dir}/storage/typed_value.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/typed_value_store.hpp DESTINATION ${build_include_dir}/storage)
|
||||
FILE(COPY ${include_dir}/storage/typed_value_utils.hpp DESTINATION ${build_include_dir}/storage)
|
||||
|
||||
FILE(COPY ${include_dir}/storage/garbage/delete_sensitive.hpp DESTINATION ${build_include_dir}/storage/garbage)
|
||||
FILE(COPY ${include_dir}/storage/garbage/garbage.hpp DESTINATION ${build_include_dir}/storage/garbage)
|
||||
|
||||
FILE(COPY ${include_dir}/utils/string_buffer.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/handle_write.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/sys.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/char_str.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/void.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/array_store.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/bswap.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/stacktrace/stacktrace.hpp DESTINATION ${build_include_dir}/utils/stacktrace)
|
||||
FILE(COPY ${include_dir}/utils/stacktrace/log.hpp DESTINATION ${build_include_dir}/utils/stacktrace)
|
||||
FILE(COPY ${include_dir}/utils/auto_scope.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/assert.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/reference_wrapper.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/underlying_cast.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/total_ordering.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/crtp.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/placeholder.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/likely.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/cpu_relax.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/counters/atomic_counter.hpp DESTINATION ${build_include_dir}/utils/counters)
|
||||
FILE(COPY ${include_dir}/utils/counters/simple_counter.hpp DESTINATION ${build_include_dir}/utils/counters)
|
||||
FILE(COPY ${include_dir}/utils/random/fast_binomial.hpp DESTINATION ${build_include_dir}/utils/random)
|
||||
FILE(COPY ${include_dir}/utils/random/xorshift128plus.hpp DESTINATION ${build_include_dir}/utils/random)
|
||||
FILE(COPY ${include_dir}/utils/datetime/timestamp.hpp DESTINATION ${build_include_dir}/utils/datetime)
|
||||
FILE(COPY ${include_dir}/utils/datetime/datetime_error.hpp DESTINATION ${build_include_dir}/utils/datetime)
|
||||
FILE(COPY ${include_dir}/utils/types/byte.hpp DESTINATION ${build_include_dir}/utils/types)
|
||||
FILE(COPY ${include_dir}/utils/option_ptr.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/option.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/border.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/order.hpp DESTINATION ${build_include_dir}/utils)
|
||||
FILE(COPY ${include_dir}/utils/numerics/saturate.hpp DESTINATION ${build_include_dir}/utils/numerics)
|
||||
FILE(COPY ${include_dir}/utils/memory/stack_allocator.hpp DESTINATION ${build_include_dir}/utils/memory)
|
||||
FILE(COPY ${include_dir}/utils/memory/block_allocator.hpp DESTINATION ${build_include_dir}/utils/memory)
|
||||
FILE(COPY ${include_dir}/utils/exceptions/basic_exception.hpp DESTINATION ${build_include_dir}/utils/exceptions)
|
||||
FILE(COPY ${include_dir}/utils/exceptions/out_of_memory.hpp DESTINATION ${build_include_dir}/utils/exceptions)
|
||||
|
||||
FILE(COPY ${include_dir}/utils/iterator/iterator_base.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/virtual_iter.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/iterator.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/composable.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/count.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/accessor.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/combined.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/count.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/filter.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/flat_map.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/for_all.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/inspect.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/iterator_accessor.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/lambda_iterator.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/limited_map.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/map.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
FILE(COPY ${include_dir}/utils/iterator/range_iterator.hpp DESTINATION ${build_include_dir}/utils/iterator)
|
||||
|
||||
FILE(COPY ${include_dir}/communication/communication.hpp DESTINATION ${build_include_dir}/communication)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/config.hpp DESTINATION ${build_include_dir}/communication/bolt/v1)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/serialization/record_stream.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/serialization)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/serialization/bolt_serializer.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/serialization)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/transport/bolt_encoder.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/transport)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/transport/chunked_buffer.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/transport)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/transport/chunked_encoder.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/transport)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/transport/socket_stream.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/transport)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/transport/stream_error.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/transport)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/packing/codes.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/packing)
|
||||
FILE(COPY ${include_dir}/communication/bolt/v1/messaging/codes.hpp DESTINATION ${build_include_dir}/communication/bolt/v1/messaging)
|
||||
|
||||
FILE(COPY ${include_dir}/io/network/socket.hpp DESTINATION ${build_include_dir}/io/network)
|
||||
FILE(COPY ${include_dir}/io/network/addrinfo.hpp DESTINATION ${build_include_dir}/io/network)
|
||||
FILE(COPY ${include_dir}/io/network/network_error.hpp DESTINATION ${build_include_dir}/io/network)
|
||||
FILE(COPY ${include_dir}/io/network/socket.hpp DESTINATION ${build_include_dir}/io/network)
|
||||
|
||||
FILE(COPY ${include_dir}/logging/default.hpp DESTINATION ${build_include_dir}/logging)
|
||||
FILE(COPY ${include_dir}/logging/log.hpp DESTINATION ${build_include_dir}/logging)
|
||||
FILE(COPY ${include_dir}/logging/logger.hpp DESTINATION ${build_include_dir}/logging)
|
||||
FILE(COPY ${include_dir}/logging/levels.hpp DESTINATION ${build_include_dir}/logging)
|
||||
FILE(COPY ${include_dir}/logging/loggable.hpp DESTINATION ${build_include_dir}/logging)
|
||||
|
||||
FILE(COPY ${include_dir}/snapshot/snapshot_engine.hpp DESTINATION ${build_include_dir}/snapshot)
|
||||
# -----------------------------------------------------------------------------
|
@ -1,18 +0,0 @@
|
||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
||||
else()
|
||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
||||
endif()
|
||||
|
||||
include(DownloadProject/DownloadProject)
|
||||
|
||||
download_project(
|
||||
PROJ googlebenchmark
|
||||
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||
GIT_TAG master
|
||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
||||
)
|
||||
|
||||
add_subdirectory(${googlebenchmark_SOURCE_DIR} ${googlebenchmark_BINARY_DIR})
|
||||
|
||||
include_directories("${googlebenchmark_SOURCE_DIR}/include")
|
@ -1,19 +0,0 @@
|
||||
if (CMAKE_VERSION VERSION_LESS 3.2)
|
||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
|
||||
else()
|
||||
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
|
||||
endif()
|
||||
|
||||
include(DownloadProject/DownloadProject)
|
||||
|
||||
download_project(
|
||||
PROJ googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
${UPDATE_DISCONNECTED_IF_AVAILABLE}
|
||||
)
|
||||
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
||||
|
||||
include_directories("${googletest_SOURCE_DIR}/googletest/include")
|
||||
include_directories("${googletest_SOURCE_DIR}/googlemock/include")
|
@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DownloadProject
|
||||
git clone https://github.com/Crascit/DownloadProject
|
||||
download_project_tag="master"
|
||||
cd DownloadProject
|
||||
git checkout ${download_project_tag}
|
||||
cd ..
|
@ -1,9 +0,0 @@
|
||||
FROM python:3.5
|
||||
|
||||
COPY src/crash_analysis/server /server
|
||||
|
||||
RUN pip install -r /server/requirements.txt
|
||||
|
||||
WORKDIR /server
|
||||
|
||||
CMD python app.py
|
@ -1,12 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "utils/terminate_handler.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
std::set_terminate(&terminate_handler);
|
||||
|
||||
throw std::runtime_error("runtime error");
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/* Memgraph communication protocol
|
||||
* gate is the first name proposal for the protocol */
|
||||
|
||||
// TODO
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/* TODO: HTTP & HTTPS implementations */
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "io/network/socket.hpp"
|
||||
|
||||
namespace io
|
||||
{
|
||||
|
||||
class TcpConnection
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class EventLoop
|
||||
{
|
||||
public:
|
||||
EventLoop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
Even
|
||||
};
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace io
|
||||
{
|
||||
|
||||
class StreamDispatcher
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <uv.h>
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
template <size_t block_size>
|
||||
class BlockBuffer
|
||||
{
|
||||
static BlockAllocator<block_size> allocator;
|
||||
|
||||
struct Block : public uv_buf_t
|
||||
{
|
||||
Block()
|
||||
{
|
||||
// acquire a new block of memory for this buffer
|
||||
base = static_cast<char*>(allocator.acquire());
|
||||
len = 0;
|
||||
}
|
||||
|
||||
~Block()
|
||||
{
|
||||
// release the block of memory previously acquired
|
||||
allocator.release(base);
|
||||
}
|
||||
|
||||
size_t append(const char* data, size_t size)
|
||||
{
|
||||
// compute the remaining capacity for this block
|
||||
auto capacity = block_size - len;
|
||||
|
||||
// if the capacity is smaller than the requested size, copy only
|
||||
// up to the remaining capacity
|
||||
if(size > capacity)
|
||||
size = capacity;
|
||||
|
||||
std::memcpy(base + len, data, size);
|
||||
len += size;
|
||||
|
||||
// return how much we've copied
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
BlockBuffer()
|
||||
{
|
||||
// create the first buffer
|
||||
buffers.emplace_back();
|
||||
}
|
||||
|
||||
~BlockBuffer()
|
||||
{
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
BlockBuffer(BlockBuffer&) = delete;
|
||||
BlockBuffer(BlockBuffer&&) = delete;
|
||||
|
||||
size_t count() const
|
||||
{
|
||||
return buffers.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
// pop all buffers except for the first one since we need to allocate
|
||||
// it again anyway so why not keep it in the first place
|
||||
while(count() > 1)
|
||||
buffers.pop_back();
|
||||
|
||||
// pretend we just allocated our first buffer and set it's length to 0
|
||||
buffers.back().len = 0;
|
||||
}
|
||||
|
||||
BlockBuffer& operator<<(const std::string& data)
|
||||
{
|
||||
append(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void append(const std::string& data)
|
||||
{
|
||||
append(data.c_str(), data.size());
|
||||
}
|
||||
|
||||
void append(const char* data, size_t size)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
// try to copy as much data as possible
|
||||
auto copied = buffers.back().append(data, size);
|
||||
|
||||
// if we managed to copy everything, we're done
|
||||
if(copied == size)
|
||||
break;
|
||||
|
||||
// move the pointer past the copied part
|
||||
data += copied;
|
||||
|
||||
// reduce the total size by the number of copied items
|
||||
size -= copied;
|
||||
|
||||
// since we ran out of space, construct a new buffer
|
||||
buffers.emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
operator uv_buf_t*()
|
||||
{
|
||||
return buffers.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Block> buffers;
|
||||
};
|
||||
|
||||
template <size_t block_size>
|
||||
BlockAllocator<block_size> BlockBuffer<block_size>::allocator;
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
using callback_t = void (*)(uv_handle_t *);
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "core.hpp"
|
||||
#include "uvloop.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class TcpStream
|
||||
{
|
||||
public:
|
||||
TcpStream(UvLoop& loop);
|
||||
|
||||
template <typename T>
|
||||
T* data();
|
||||
|
||||
template <typename T>
|
||||
void data(T* value);
|
||||
|
||||
void close(callback_t callback);
|
||||
|
||||
operator uv_handle_t*();
|
||||
operator uv_tcp_t*();
|
||||
operator uv_stream_t*();
|
||||
|
||||
private:
|
||||
uv_tcp_t stream;
|
||||
};
|
||||
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#include "tcpstream.hpp"
|
||||
#include "uvbuffer.hpp"
|
||||
#include "uvloop.hpp"
|
||||
#include "blockbuffer.hpp"
|
||||
|
||||
#include "tcpstream.inl"
|
||||
#include "uvbuffer.inl"
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UvError(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <uv.h>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvBuffer
|
||||
{
|
||||
public:
|
||||
UvBuffer();
|
||||
UvBuffer(size_t capacity);
|
||||
UvBuffer(const std::string& data);
|
||||
|
||||
~UvBuffer();
|
||||
|
||||
size_t size() const noexcept;
|
||||
size_t length() const noexcept;
|
||||
|
||||
void clear();
|
||||
|
||||
UvBuffer& append(const std::string& data);
|
||||
UvBuffer& append(const char* data, size_t n);
|
||||
|
||||
UvBuffer& operator<<(const std::string& data);
|
||||
|
||||
operator uv_buf_t*();
|
||||
|
||||
private:
|
||||
uv_buf_t buffer;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <uv.h>
|
||||
|
||||
#include "uv_error.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvLoop final
|
||||
{
|
||||
public:
|
||||
using sptr = std::shared_ptr<UvLoop>;
|
||||
|
||||
enum Mode {
|
||||
Default = UV_RUN_DEFAULT,
|
||||
Once = UV_RUN_ONCE,
|
||||
NoWait = UV_RUN_NOWAIT
|
||||
};
|
||||
|
||||
UvLoop()
|
||||
{
|
||||
uv_loop = uv_default_loop();
|
||||
|
||||
if(uv_loop == nullptr)
|
||||
throw UvError("Failed to initialize libuv event loop");
|
||||
}
|
||||
|
||||
~UvLoop()
|
||||
{
|
||||
uv_loop_close(uv_loop);
|
||||
}
|
||||
|
||||
bool run(Mode mode)
|
||||
{
|
||||
return uv_run(uv_loop, static_cast<uv_run_mode>(mode));
|
||||
}
|
||||
|
||||
bool alive()
|
||||
{
|
||||
return uv_loop_alive(uv_loop);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
uv_stop(uv_loop);
|
||||
}
|
||||
|
||||
operator uv_loop_t*()
|
||||
{
|
||||
return uv_loop;
|
||||
}
|
||||
|
||||
private:
|
||||
uv_loop_t* uv_loop;
|
||||
};
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/ir/tree/node.hpp"
|
||||
|
||||
class Backend
|
||||
{
|
||||
public:
|
||||
virtual void process(ir::Node *node) = 0;
|
||||
virtual ~Backend() {}
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/backend.hpp"
|
||||
|
||||
class CodeGenerator : public Backend
|
||||
{
|
||||
private:
|
||||
struct Code
|
||||
{
|
||||
public:
|
||||
void println(const std::string &line)
|
||||
{
|
||||
code.append(line + "\n");
|
||||
line_no++;
|
||||
}
|
||||
|
||||
void save(const std::string &)
|
||||
{
|
||||
throw std::runtime_error("TODO: implementation");
|
||||
}
|
||||
|
||||
void reset() { code = ""; }
|
||||
|
||||
std::string code;
|
||||
uint64_t line_no;
|
||||
};
|
||||
|
||||
protected:
|
||||
Code code;
|
||||
|
||||
public:
|
||||
void emit(const std::string &line) { code.println(line); }
|
||||
|
||||
void save(const std::string &path) { code.save(path); }
|
||||
|
||||
void reset() { code.reset(); }
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/backend/code_generator.hpp"
|
||||
|
||||
/*
|
||||
* Traverses the intermediate representation tree and generates
|
||||
* C++ code.
|
||||
*/
|
||||
class CppCodeGenerator : public CodeGenerator
|
||||
{
|
||||
public:
|
||||
CppCodeGenerator()
|
||||
{
|
||||
throw std::runtime_error("TODO: implementation");
|
||||
}
|
||||
|
||||
void process(ir::Node *) override
|
||||
{
|
||||
throw std::runtime_error("TODO: implementation");
|
||||
|
||||
}
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: implementation
|
@ -1,222 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <experimental/filesystem>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
#include "utils/exceptions/not_yet_implemented.hpp"
|
||||
#include "config/config.hpp"
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/exception/query_engine.hpp"
|
||||
#include "query/plan_compiler.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "query/preprocessor.hpp"
|
||||
#include "utils/dynamic_lib.hpp"
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
|
||||
// TODO: replace with openCypher and Antlr
|
||||
#include "query/frontend/cypher.hpp"
|
||||
|
||||
/**
|
||||
* Responsible for query execution.
|
||||
*
|
||||
* Current Query Engine arhitecture:
|
||||
* query -> query_stripper -> [plan_generator] -> [plan_compiler] -> execution
|
||||
*
|
||||
* @tparam Stream the query engine has to be aware of the Stream because Stream
|
||||
* is passed to the dynamic shared library because that is the way how
|
||||
* the results should be returned (more optimal then just return
|
||||
* the whole result set)
|
||||
*/
|
||||
template <typename Stream>
|
||||
class QueryEngine : public Loggable
|
||||
{
|
||||
private:
|
||||
using QueryPlanLib = DynamicLib<QueryPlanTrait<Stream>>;
|
||||
|
||||
public:
|
||||
QueryEngine() : Loggable("QueryEngine") {}
|
||||
|
||||
/**
|
||||
* Reloads query plan (plan_path contains compiled query plan).
|
||||
* This methdo just calculates stripped query and offloads everything else
|
||||
* to the LoadCpp method.
|
||||
*
|
||||
* @param query a query for which the plan will be loaded
|
||||
* @param plan_path a custom made cpp query plan
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
auto ReloadCustom(const std::string &query, const fs::path &plan_path)
|
||||
{
|
||||
auto preprocessed = preprocessor.preprocess(query);
|
||||
Unload(query);
|
||||
LoadCpp(plan_path, preprocessed.hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query on the database with the stream. If a query plan is cached
|
||||
* (based on query hash) it will be used.
|
||||
*
|
||||
* @param query a query that is going to be executed
|
||||
* @param db database againt the query is going to be executed
|
||||
* @param stream the resuts will be send to the stream
|
||||
*
|
||||
* @return query execution status:
|
||||
* false if query wasn't executed successfully
|
||||
* true if query execution was successfull
|
||||
*/
|
||||
auto Run(const std::string &query, GraphDbAccessor &db_accessor, Stream &stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto preprocessed = preprocessor.preprocess(query);
|
||||
auto plan = LoadCypher(preprocessed);
|
||||
auto result = plan->run(db_accessor, preprocessed.arguments, stream);
|
||||
if (UNLIKELY(!result))
|
||||
{
|
||||
// info because it might be something like deadlock in which
|
||||
// case one thread is stopped and user has try again
|
||||
logger.info(
|
||||
"Unable to execute query (execution returned false)");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (CypherLexicalError &e)
|
||||
{
|
||||
logger.error("CypherLexicalError: {}", std::string(e.what()));
|
||||
throw e;
|
||||
}
|
||||
catch (QueryEngineException &e)
|
||||
{
|
||||
logger.error("QueryEngineException: {}", std::string(e.what()));
|
||||
throw e;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
throw BasicException(e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw BasicException("unknown query engine exception");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads query plan and release the resources (should be automatically).
|
||||
*
|
||||
* @param query a query for which the query plan will be unloaded.
|
||||
*
|
||||
* return bool is the plan unloaded
|
||||
*/
|
||||
auto Unload(const std::string &query)
|
||||
{
|
||||
return query_plans.access().remove(preprocessor.preprocess(query).hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is a plan for the query loaded.
|
||||
*
|
||||
* @param query for which a plan existance will be checked
|
||||
*
|
||||
* return bool
|
||||
*/
|
||||
auto Loaded(const std::string &query)
|
||||
{
|
||||
auto plans_accessor = query_plans.access();
|
||||
return plans_accessor.find(preprocessor.preprocess(query).hash) !=
|
||||
plans_accessor.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of loaded query plans.
|
||||
*
|
||||
* @return size_t the number of loaded query plans
|
||||
*/
|
||||
auto Size() { // TODO: const once whan ConcurrentMap::Accessor becomes const
|
||||
return query_plans.access().size();
|
||||
}
|
||||
// return query_plans.access().size(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Loads query plan eather from hardcoded folder or from the file that is
|
||||
* generated in this method.
|
||||
*
|
||||
* @param stripped a stripped query
|
||||
*
|
||||
* @return runnable query plan
|
||||
*/
|
||||
auto LoadCypher(const StrippedQuery &stripped)
|
||||
{
|
||||
auto plans_accessor = query_plans.access();
|
||||
|
||||
// code is already compiled and loaded, just return runnable
|
||||
// instance
|
||||
auto query_plan_it = plans_accessor.find(stripped.hash);
|
||||
if (query_plan_it != plans_accessor.end())
|
||||
return query_plan_it->second->instance();
|
||||
|
||||
// find hardcoded query plan if exists
|
||||
auto hardcoded_path =
|
||||
fs::path(CONFIG(config::COMPILE_PATH) + "hardcode/" +
|
||||
std::to_string(stripped.hash) + ".cpp");
|
||||
if (fs::exists(hardcoded_path))
|
||||
return LoadCpp(hardcoded_path, stripped.hash);
|
||||
|
||||
// generate query plan
|
||||
auto generated_path =
|
||||
fs::path(CONFIG(config::COMPILE_PATH) + std::to_string(stripped.hash) + ".cpp");
|
||||
// TODO implement CPP generator
|
||||
// plan_generator.generate_plan(stripped.query, stripped.hash, generated_path);
|
||||
throw NotYetImplemented();
|
||||
return LoadCpp(generated_path, stripped.hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load cpp query plan from a file. Or if plan is already cached from the
|
||||
* cache.
|
||||
*
|
||||
* @param path_cpp a path to query plan
|
||||
* @param hash query hash
|
||||
*
|
||||
* @return runnable query plan
|
||||
*/
|
||||
auto LoadCpp(const fs::path &path_cpp, const HashType hash)
|
||||
{
|
||||
auto plans_accessor = query_plans.access();
|
||||
|
||||
// code is already compiled and loaded, just return runnable
|
||||
// instance
|
||||
auto query_plan_it = plans_accessor.find(hash);
|
||||
if (query_plan_it != plans_accessor.end())
|
||||
return query_plan_it->second->instance();
|
||||
|
||||
// generate dynamic lib path
|
||||
// The timestamp has been added here because dlopen
|
||||
// uses path and returns the same handler for the same path
|
||||
// and that is a problem because at this point we want brand new
|
||||
// dynamic lib. That is the tmp solution. The right solution would be
|
||||
// to deal with this problem in DynamicLib
|
||||
auto path_so = CONFIG(config::COMPILE_PATH) + std::to_string(hash) +
|
||||
"_" + (std::string)Timestamp::now() + ".so";
|
||||
|
||||
plan_compiler.compile(path_cpp, path_so);
|
||||
|
||||
auto query_plan = std::make_unique<QueryPlanLib>(path_so);
|
||||
// TODO: underlying object has to be live during query execution
|
||||
// fix that when Antler will be introduced into the database
|
||||
|
||||
auto query_plan_instance = query_plan->instance(); // because of move
|
||||
plans_accessor.insert(hash, std::move(query_plan));
|
||||
|
||||
// return an instance of runnable code (PlanInterface)
|
||||
return query_plan_instance;
|
||||
}
|
||||
|
||||
QueryPreprocessor preprocessor;
|
||||
PlanCompiler plan_compiler;
|
||||
ConcurrentMap<HashType, std::unique_ptr<QueryPlanLib>>
|
||||
query_plans;
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/language/cypher/compiler.hpp"
|
||||
|
||||
// DEPRICATED
|
||||
|
||||
namespace cypher
|
||||
{
|
||||
|
||||
class Frontend
|
||||
{
|
||||
public:
|
||||
Frontend() {}
|
||||
|
||||
auto generate_ir(const std::string& query)
|
||||
{
|
||||
try {
|
||||
return compiler.syntax_tree(query);
|
||||
} catch (const BasicException &e) {
|
||||
// logger.error("Parsing error while processing query: {}", query);
|
||||
// logger.error(std::string(e.what()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cypher::Compiler compiler;
|
||||
};
|
||||
|
||||
}
|
@ -1,611 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "logging/default.hpp"
|
||||
#include "query/backend/cpp_old/code.hpp"
|
||||
#include "query/backend/cpp_old/cpp_generator.hpp"
|
||||
#include "query/backend/cpp_old/entity_search.hpp"
|
||||
#include "query/backend/cpp_old/structures.hpp"
|
||||
#include "query/language/cypher/errors.hpp"
|
||||
#include "query/language/cypher/visitor/traverser.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;
|
||||
LabelSetElement labels_set_element;
|
||||
PropertyState property_state;
|
||||
|
||||
void clear_state()
|
||||
{
|
||||
set_element_state.clear();
|
||||
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;
|
||||
|
||||
void finish_query_execution()
|
||||
{
|
||||
generator.add_action(QueryAction::TransactionCommit);
|
||||
|
||||
code += generator.generate();
|
||||
|
||||
generator.clear();
|
||||
}
|
||||
|
||||
Logger logger;
|
||||
|
||||
public:
|
||||
CppTraverser() : logger(logging::log->logger("CppTraverser")) {}
|
||||
|
||||
void semantic_check() const
|
||||
{
|
||||
if (!has_return)
|
||||
throw CypherSemanticError(
|
||||
"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
|
||||
// only at the one place)
|
||||
generator.add_action(QueryAction::TransactionBegin);
|
||||
|
||||
Traverser::visit(write_query);
|
||||
|
||||
// TODO: put this inside the top level mentioned above
|
||||
finish_query_execution();
|
||||
}
|
||||
|
||||
void visit(ast::ReadQuery &read_query) override
|
||||
{
|
||||
generator.add_action(QueryAction::TransactionBegin);
|
||||
|
||||
Traverser::visit(read_query);
|
||||
|
||||
finish_query_execution();
|
||||
}
|
||||
|
||||
void visit(ast::UpdateQuery &update_query) override
|
||||
{
|
||||
generator.add_action(QueryAction::TransactionBegin);
|
||||
|
||||
Traverser::visit(update_query);
|
||||
|
||||
finish_query_execution();
|
||||
}
|
||||
|
||||
void visit(ast::DeleteQuery &delete_query) override
|
||||
{
|
||||
generator.add_action(QueryAction::TransactionBegin);
|
||||
|
||||
Traverser::visit(delete_query);
|
||||
|
||||
finish_query_execution();
|
||||
}
|
||||
|
||||
void visit(ast::ReadWriteQuery &read_write_query) override
|
||||
{
|
||||
generator.add_action(QueryAction::TransactionBegin);
|
||||
|
||||
Traverser::visit(read_write_query);
|
||||
|
||||
finish_query_execution();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
code += generator.generate();
|
||||
}
|
||||
|
||||
void visit(ast::Return &ast_return) override
|
||||
{
|
||||
has_return = true;
|
||||
|
||||
generator.add_action(QueryAction::Return);
|
||||
|
||||
state = CypherState::Return;
|
||||
query_action = QueryAction::Return;
|
||||
|
||||
Traverser::visit(ast_return);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void visit(ast::Pattern &ast_pattern) override
|
||||
{
|
||||
// TODO: Is that traversal order OK for all cases? Probably NOT.
|
||||
if (ast_pattern.has_next())
|
||||
{
|
||||
visit(*ast_pattern.next);
|
||||
visit(*ast_pattern.node);
|
||||
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);
|
||||
|
||||
if (cypher_data.status(name) != EntityStatus::Matched &&
|
||||
state == CypherState::Create)
|
||||
{
|
||||
cypher_data.node_created(name);
|
||||
}
|
||||
}
|
||||
|
||||
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 value will be index inside the 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();
|
||||
auto &action_data = generator.action_data();
|
||||
|
||||
if (!ast_relationship.has_name()) return;
|
||||
entity = ast_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;
|
||||
|
||||
// TODO: add suport for Direction::Both
|
||||
|
||||
// TODO: simplify somehow
|
||||
if (state == CypherState::Create)
|
||||
{
|
||||
if (!cypher_data.exist(entity))
|
||||
{
|
||||
clause_action = ClauseAction::CreateRelationship;
|
||||
create_relationship(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (state == CypherState::Match)
|
||||
{
|
||||
if (!cypher_data.exist(entity))
|
||||
{
|
||||
action_data.actions[entity] = ClauseAction::MatchRelationship;
|
||||
}
|
||||
}
|
||||
|
||||
Traverser::visit(ast_relationship);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == CypherState::Create)
|
||||
{
|
||||
if (ast_relationship_specs.has_identifier())
|
||||
{
|
||||
entity = ast_relationship_specs.name();
|
||||
}
|
||||
}
|
||||
|
||||
Traverser::visit(ast_relationship_specs);
|
||||
}
|
||||
|
||||
void visit(ast::RelationshipTypeList &ast_relationship_type_list) override
|
||||
{
|
||||
auto &action_data = generator.action_data();
|
||||
|
||||
if (ast_relationship_type_list.has_value())
|
||||
{
|
||||
auto type = ast_relationship_type_list.value->name;
|
||||
action_data.add_entity_tag(entity, type);
|
||||
action_data.csm.search_cost(entity,
|
||||
entity_search::search_type_index,
|
||||
entity_search::type_cost);
|
||||
}
|
||||
|
||||
Traverser::visit(ast_relationship_type_list);
|
||||
}
|
||||
|
||||
void visit(ast::LabelList &ast_label_list) override
|
||||
{
|
||||
if (state == CypherState::Set)
|
||||
{
|
||||
clause_action = ClauseAction::UpdateEntityLabels_Labels;
|
||||
Traverser::visit(ast_label_list);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &action_data = generator.action_data();
|
||||
auto &cypher_data = generator.cypher_data();
|
||||
|
||||
if (!ast_label_list.has_value()) return;
|
||||
|
||||
auto label = ast_label_list.value->name;
|
||||
|
||||
action_data.add_entity_tag(entity, label);
|
||||
action_data.csm.search_cost(entity, entity_search::search_label_index,
|
||||
entity_search::label_cost);
|
||||
cypher_data.tag(entity, label);
|
||||
// TODO: it shouldn't be decided here
|
||||
cypher_data.source(entity, EntitySource::LabelIndex);
|
||||
|
||||
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;
|
||||
|
||||
// update action data
|
||||
auto &action_data = generator.action_data();
|
||||
action_data.parameter_index.emplace(ParameterIndexKey(entity, prop),
|
||||
index);
|
||||
action_data.add_entitiy_property(entity, prop);
|
||||
|
||||
// update cypher data
|
||||
auto &cypher_data = generator.cypher_data();
|
||||
cypher_data.index(entity, prop, index);
|
||||
|
||||
clear_state();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (state == CypherState::Set &&
|
||||
clause_action == ClauseAction::UpdateEntityLabels_Identifier)
|
||||
{
|
||||
labels_set_element.entity = ast_identifier.name;
|
||||
}
|
||||
|
||||
if (state == CypherState::Set &&
|
||||
clause_action == ClauseAction::UpdateEntityLabels_Labels)
|
||||
{
|
||||
labels_set_element.labels.emplace_back(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::None)
|
||||
throw CypherSemanticError("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;
|
||||
|
||||
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;
|
||||
|
||||
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::LabelSetElement &ast_label_set_element) override
|
||||
{
|
||||
clause_action = ClauseAction::UpdateEntityLabels_Identifier;
|
||||
|
||||
labels_set_element.clear();
|
||||
|
||||
Traverser::visit(ast_label_set_element);
|
||||
|
||||
auto &action_data = generator.action_data();
|
||||
|
||||
action_data.label_set_elements.emplace_back(std::move(labels_set_element));
|
||||
|
||||
clause_action = ClauseAction::Undefined;
|
||||
}
|
||||
|
||||
void visit(ast::Delete &ast_delete) override
|
||||
{
|
||||
code += generator.generate();
|
||||
|
||||
state = CypherState::Delete;
|
||||
|
||||
auto &action_data = generator.add_action(QueryAction::Delete);
|
||||
|
||||
Traverser::visit(ast_delete);
|
||||
|
||||
code += generator.generate();
|
||||
}
|
||||
|
||||
void visit(ast::CountFunction &ast_count) override
|
||||
{
|
||||
auto &action_data = generator.action_data();
|
||||
auto &cypher_data = generator.cypher_data();
|
||||
|
||||
// if (state == CypherState::Return)
|
||||
// {
|
||||
action_data.actions[ast_count.argument] = ClauseAction::ReturnCount;
|
||||
// }
|
||||
}
|
||||
|
||||
void visit(ast::LabelsFunction &ast_label) override
|
||||
{
|
||||
auto &action_data = generator.action_data();
|
||||
action_data.actions[ast_label.argument] = ClauseAction::ReturnLabels;
|
||||
}
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace opencypher
|
||||
{
|
||||
|
||||
class Fronted
|
||||
{
|
||||
};
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: implementation
|
8
include/query/language/cypher/.gitignore
vendored
8
include/query/language/cypher/.gitignore
vendored
@ -1,8 +0,0 @@
|
||||
*.o
|
||||
cypher.cpp
|
||||
cypher.h
|
||||
cypher.hpp
|
||||
parser
|
||||
cypher.out
|
||||
parser
|
||||
sqlite.y
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "expr.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Accessor : public ValueExpr<Accessor>
|
||||
{
|
||||
Accessor(Identifier* entity, Identifier* prop)
|
||||
: entity(entity), prop(prop) {}
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
// TODO: set identifier as base class
|
||||
struct Alias : public AstNode<Alias>
|
||||
{
|
||||
Alias(std::string name, std::string alias)
|
||||
: name(name), alias(alias) {}
|
||||
|
||||
std::string name;
|
||||
std::string alias;
|
||||
};
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "accessor.hpp"
|
||||
#include "values.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "alias.hpp"
|
||||
#include "operators.hpp"
|
||||
#include "property.hpp"
|
||||
#include "relationship.hpp"
|
||||
#include "node.hpp"
|
||||
#include "distinct.hpp"
|
||||
#include "return.hpp"
|
||||
#include "pattern.hpp"
|
||||
#include "return.hpp"
|
||||
#include "delete.hpp"
|
||||
#include "match.hpp"
|
||||
#include "queries.hpp"
|
||||
#include "start.hpp"
|
||||
#include "set.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "with.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "merge.hpp"
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ast_visitor.hpp"
|
||||
#include "values.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
class AstEcho : public AstVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void visit(Boolean& node)
|
||||
{
|
||||
std::cout << "Boolean: " << node.value << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "utils/visitor/visitable.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
#include "ast_visitor.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct AstVisitor;
|
||||
|
||||
struct AstVisitable : public Visitable<AstVisitor>
|
||||
{
|
||||
using uptr = std::unique_ptr<AstVisitable>;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct AstNode : public Crtp<Derived>, public AstVisitable
|
||||
{
|
||||
using uptr = std::unique_ptr<Derived>;
|
||||
|
||||
void accept(AstVisitor& visitor) override
|
||||
{
|
||||
visitor.visit(this->derived());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/visitor/visitor.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Identifier;
|
||||
struct IdentifierList;
|
||||
struct Alias;
|
||||
|
||||
// properties
|
||||
struct Property;
|
||||
struct PropertyList;
|
||||
struct Accessor;
|
||||
|
||||
// values
|
||||
struct Boolean;
|
||||
struct Float;
|
||||
struct Integer;
|
||||
struct String;
|
||||
struct Long;
|
||||
|
||||
// operators
|
||||
struct And;
|
||||
struct Or;
|
||||
struct Lt;
|
||||
struct Gt;
|
||||
struct Ge;
|
||||
struct Le;
|
||||
struct Eq;
|
||||
struct Ne;
|
||||
struct Plus;
|
||||
struct Minus;
|
||||
struct Star;
|
||||
struct Slash;
|
||||
struct Rem;
|
||||
|
||||
// functions
|
||||
struct CountFunction;
|
||||
struct LabelsFunction;
|
||||
|
||||
struct RelationshipSpecs;
|
||||
struct RelationshipTypeList;
|
||||
struct Relationship;
|
||||
|
||||
struct Node;
|
||||
struct LabelList;
|
||||
struct Pattern;
|
||||
struct PatternExpr;
|
||||
struct PatternList;
|
||||
|
||||
struct Return;
|
||||
struct ReturnList;
|
||||
struct Distinct;
|
||||
|
||||
struct Create;
|
||||
struct Merge;
|
||||
struct Match;
|
||||
struct Where;
|
||||
struct Set;
|
||||
struct Delete;
|
||||
|
||||
struct Start;
|
||||
struct WriteQuery;
|
||||
struct ReadQuery;
|
||||
struct UpdateQuery;
|
||||
struct DeleteQuery;
|
||||
struct ReadWriteQuery;
|
||||
struct MergeQuery;
|
||||
|
||||
struct SetKey;
|
||||
struct SetValue;
|
||||
struct SetElement;
|
||||
struct LabelSetElement;
|
||||
struct SetList;
|
||||
|
||||
struct WithList;
|
||||
struct WithClause;
|
||||
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, 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,
|
||||
CountFunction, LabelsFunction, InternalIdExpr, SetValue, SetElement,
|
||||
MergeQuery, Merge, LabelSetElement, SetList>
|
||||
{
|
||||
};
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "pattern.hpp"
|
||||
#include "return.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Create : public AstNode<Create>
|
||||
{
|
||||
Create(Pattern* pattern)
|
||||
: pattern(pattern) {}
|
||||
|
||||
Pattern* pattern;
|
||||
};
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Delete : public AstNode<Delete>
|
||||
{
|
||||
Delete(Identifier* identifier, bool is_detached = false)
|
||||
: identifier(identifier), is_detached(is_detached) {}
|
||||
|
||||
Identifier* identifier;
|
||||
bool is_detached;
|
||||
};
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Distinct : public AstNode<Distinct>
|
||||
{
|
||||
Distinct(Identifier* identifier)
|
||||
: identifier(identifier) {}
|
||||
|
||||
Identifier* identifier;
|
||||
};
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Expr : public AstVisitable
|
||||
{
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct ValueExpr : public Crtp<Derived>, public Expr
|
||||
{
|
||||
using uptr = std::unique_ptr<Derived>;
|
||||
|
||||
virtual void accept(AstVisitor &visitor) { visitor.visit(this->derived()); }
|
||||
};
|
||||
|
||||
template <class T, class Derived>
|
||||
struct LeafExpr : public ValueExpr<Derived>
|
||||
{
|
||||
LeafExpr(T value) : value(value) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
// T is argument type
|
||||
template <class T, class Derived>
|
||||
struct FunctionExpr : public ValueExpr<Derived>
|
||||
{
|
||||
FunctionExpr(const std::string& name, T argument) : name(name), argument(argument) {}
|
||||
|
||||
std::string name;
|
||||
T argument;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct BinaryExpr : public ValueExpr<Derived>
|
||||
{
|
||||
BinaryExpr(Expr *left, Expr *right) : left(left), right(right) {}
|
||||
|
||||
Expr *left;
|
||||
Expr *right;
|
||||
};
|
||||
|
||||
struct PatternExpr : public Expr
|
||||
{
|
||||
using uptr = std::unique_ptr<PatternExpr>;
|
||||
|
||||
PatternExpr(Pattern *pattern) : pattern(pattern) {}
|
||||
|
||||
virtual void accept(AstVisitor &visitor) { visitor.visit(*this); }
|
||||
|
||||
Pattern *pattern;
|
||||
};
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct CountFunction : public FunctionExpr<std::string, CountFunction>
|
||||
{
|
||||
CountFunction(const std::string &argument) : FunctionExpr("count", argument)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct LabelsFunction : public FunctionExpr<std::string, LabelsFunction>
|
||||
{
|
||||
LabelsFunction(const std::string &argument) : FunctionExpr("labels", argument)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Identifier : public AstNode<Identifier>
|
||||
{
|
||||
Identifier(std::string name)
|
||||
: name(name) {}
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct IdentifierList : public List<Identifier, IdentifierList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
template <class T, class Derived>
|
||||
struct List : public AstNode<Derived>
|
||||
{
|
||||
List(T* value, Derived* next)
|
||||
: value(value), next(next) {}
|
||||
|
||||
T* value;
|
||||
Derived* next;
|
||||
|
||||
bool has_value() const { return value != nullptr; }
|
||||
bool has_next() const { return next != nullptr; }
|
||||
};
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "pattern.hpp"
|
||||
#include "where.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Match : public AstNode<Match>
|
||||
{
|
||||
Match(PatternList* pattern_list, Where* where)
|
||||
: pattern_list(pattern_list), where(where) {}
|
||||
|
||||
PatternList* pattern_list;
|
||||
Where* where;
|
||||
};
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "pattern.hpp"
|
||||
#include "return.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Merge : public AstNode<Merge>
|
||||
{
|
||||
Merge(Pattern* pattern)
|
||||
: pattern(pattern) {}
|
||||
|
||||
Pattern* pattern;
|
||||
};
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct LabelList : public List<Identifier, LabelList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct Node : public AstNode<Node>
|
||||
{
|
||||
Node(Identifier* idn, LabelList* labels, PropertyList* props)
|
||||
: idn(idn), labels(labels), props(props) {}
|
||||
|
||||
Identifier* idn;
|
||||
LabelList* labels;
|
||||
PropertyList* props;
|
||||
|
||||
bool has_identifier() const
|
||||
{
|
||||
return idn != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct And : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Or : public BinaryExpr<Or>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Lt : public BinaryExpr<Lt>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Gt : public BinaryExpr<Gt>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Ge : public BinaryExpr<Ge>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Le : public BinaryExpr<Le>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Eq : public BinaryExpr<Eq>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Ne : public BinaryExpr<Ne>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Plus : public BinaryExpr<Plus>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Minus : public BinaryExpr<Minus>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Star : public BinaryExpr<Star>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Slash : public BinaryExpr<Slash>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Rem : public BinaryExpr<Rem>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.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)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Property : public AstNode<Property>
|
||||
{
|
||||
Property(Identifier* idn, Expr* value)
|
||||
: idn(idn), value(value) {}
|
||||
|
||||
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>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "create.hpp"
|
||||
#include "delete.hpp"
|
||||
#include "match.hpp"
|
||||
#include "return.hpp"
|
||||
#include "set.hpp"
|
||||
#include "merge.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct WriteQuery : public AstNode<WriteQuery>
|
||||
{
|
||||
WriteQuery(Create *create, Return *return_clause)
|
||||
: create(create), return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Create *create;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
struct ReadQuery : public AstNode<ReadQuery>
|
||||
{
|
||||
ReadQuery(Match *match, Return *return_clause)
|
||||
: match(match), return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Match *match;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
struct UpdateQuery : public AstNode<UpdateQuery>
|
||||
{
|
||||
UpdateQuery(Match *match_clause, Set *set_clause, Return *return_clause)
|
||||
: match_clause(match_clause), set_clause(set_clause),
|
||||
return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Match *match_clause;
|
||||
Set *set_clause;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
struct DeleteQuery : public AstNode<DeleteQuery>
|
||||
{
|
||||
DeleteQuery(Match *match, Delete *delete_clause)
|
||||
: match(match), delete_clause(delete_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Match *match;
|
||||
Delete *delete_clause;
|
||||
};
|
||||
|
||||
struct ReadWriteQuery : public AstNode<ReadWriteQuery>
|
||||
{
|
||||
ReadWriteQuery(Match *match_clause,
|
||||
Create *create_clause, Return *return_clause)
|
||||
: match_clause(match_clause),
|
||||
create_clause(create_clause), return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Match *match_clause;
|
||||
Create *create_clause;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
struct MergeQuery : public AstNode<MergeQuery>
|
||||
{
|
||||
MergeQuery(Merge* merge_clause, Set* set_clause, Return* return_clause) :
|
||||
merge_clause(merge_clause), set_clause(set_clause),
|
||||
return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Merge *merge_clause;
|
||||
Set *set_clause;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "identifier.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct RelationshipTypeList : public List<Identifier, RelationshipTypeList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct RelationshipSpecs : public AstNode<RelationshipSpecs>
|
||||
{
|
||||
RelationshipSpecs(Identifier *idn, RelationshipTypeList *types,
|
||||
PropertyList *props)
|
||||
: idn(idn), types(types), props(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
|
||||
};
|
||||
|
||||
Relationship(RelationshipSpecs *specs, Direction direction)
|
||||
: specs(specs), direction(direction)
|
||||
{
|
||||
}
|
||||
|
||||
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(); }
|
||||
};
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "list.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "distinct.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct ReturnList : public List<Expr, ReturnList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct Return : public AstNode<Return>
|
||||
{
|
||||
Return(ReturnList* return_list)
|
||||
: return_list(return_list), distinct(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Return(Distinct* distinct)
|
||||
: return_list(nullptr), distinct(distinct)
|
||||
{
|
||||
}
|
||||
|
||||
ReturnList* return_list;
|
||||
Distinct* distinct;
|
||||
};
|
||||
|
||||
};
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "accessor.hpp"
|
||||
#include "list.hpp"
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
//
|
||||
// SetList
|
||||
// ^
|
||||
// |
|
||||
// |------------------------------------|
|
||||
// SetElement
|
||||
// ^
|
||||
// |
|
||||
// |-------------------|
|
||||
// Accessor SetValue
|
||||
// ^ ^
|
||||
// | |
|
||||
// |-------| |-------|
|
||||
// SET n.name = "Paul", n.surname = "Scholes"
|
||||
// ^ ^
|
||||
// | |
|
||||
// element entity element property
|
||||
//
|
||||
|
||||
struct SetValue : public AstNode<SetValue>
|
||||
{
|
||||
SetValue(Expr* value)
|
||||
: value(value) {}
|
||||
|
||||
Expr* value;
|
||||
|
||||
bool has_value() const { return value != nullptr; }
|
||||
};
|
||||
|
||||
struct SetElementBase : public AstVisitable
|
||||
{
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct SetElementDerivedBase : public Crtp<Derived>, public SetElementBase
|
||||
{
|
||||
using uptr = std::unique_ptr<Derived>;
|
||||
|
||||
virtual void accept(AstVisitor &visitor) { visitor.visit(this->derived()); }
|
||||
};
|
||||
|
||||
struct SetElement : public SetElementDerivedBase<SetElement>
|
||||
{
|
||||
SetElement(Accessor* accessor, SetValue* set_value)
|
||||
: accessor(accessor), set_value(set_value) {}
|
||||
|
||||
Accessor* accessor;
|
||||
SetValue* set_value;
|
||||
|
||||
bool has_accessor() const { return accessor != nullptr; }
|
||||
bool has_value() const { return set_value != nullptr; }
|
||||
};
|
||||
|
||||
struct LabelSetElement : public SetElementDerivedBase<LabelSetElement>
|
||||
{
|
||||
LabelSetElement(Identifier* identifier, LabelList* label_list)
|
||||
: identifier(identifier), label_list(label_list) {}
|
||||
|
||||
Identifier* identifier;
|
||||
LabelList* label_list;
|
||||
|
||||
bool has_identifier() const { return identifier != nullptr; }
|
||||
bool has_label_list() const { return label_list != nullptr; }
|
||||
};
|
||||
|
||||
struct SetList : public List<SetElementBase, SetList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct Set : public AstNode<Set>
|
||||
{
|
||||
Set(SetList* set_list)
|
||||
: set_list(set_list) {}
|
||||
|
||||
SetList* set_list;
|
||||
};
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "queries.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Start : public AstNode<Start>
|
||||
{
|
||||
};
|
||||
|
||||
};
|
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
class Ast
|
||||
{
|
||||
public:
|
||||
Ast() = default;
|
||||
Ast(const Ast&) = delete;
|
||||
|
||||
Ast(Ast&& other) : root(other.root), items(std::move(other.items))
|
||||
{
|
||||
other.root = nullptr;
|
||||
}
|
||||
|
||||
Ast& operator=(Ast&& other)
|
||||
{
|
||||
// TODO: write this more appropriate
|
||||
// here is CP of above code
|
||||
if(this != &other) {
|
||||
this->root = other.root;
|
||||
other.root = nullptr;
|
||||
this->items = std::move(other.items);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AstVisitable* root;
|
||||
|
||||
void traverse(AstVisitor& visitor)
|
||||
{
|
||||
root->accept(visitor);
|
||||
}
|
||||
|
||||
template <class T, typename... Args>
|
||||
T* create(Args&&... args)
|
||||
{
|
||||
auto node = new T(std::forward<Args>(args)...);
|
||||
items.push_back(std::unique_ptr<AstVisitable>(node));
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
// basically a gc vector that destroys all ast nodes once this object is
|
||||
// destroyed. parser generator is written in c and works only with raw
|
||||
// pointers so this is what makes it leak free after the parser finishes
|
||||
// parsing without using shared pointers
|
||||
std::vector<AstVisitable::uptr> items;
|
||||
};
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Float : public LeafExpr<double, Float>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct Integer : public LeafExpr<int, Integer>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct Long : public LeafExpr<int64_t, Long>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct ULong : public LeafExpr<uint64_t, ULong>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct Boolean : public LeafExpr<bool, Boolean>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct String : public LeafExpr<std::string, String>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct InternalIdExpr : public Expr
|
||||
{
|
||||
InternalIdExpr(Identifier *entity, Long *id)
|
||||
: entity(entity), id(id)
|
||||
{
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Where : public AstNode<Where>
|
||||
{
|
||||
Where(Expr* expr)
|
||||
: expr(expr) {}
|
||||
|
||||
Expr* expr;
|
||||
};
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "match.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct WithClause : public AstNode<WithClause>
|
||||
{
|
||||
WithClause(IdentifierList *identifier_list, Match *match_clause)
|
||||
: identifier_list(identifier_list), match_clause(match_clause)
|
||||
{
|
||||
}
|
||||
|
||||
IdentifierList *identifier_list;
|
||||
Match *match_clause;
|
||||
};
|
||||
|
||||
struct WithList : public List<WithClause, WithList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct WithQuery : public AstNode<WithQuery>
|
||||
{
|
||||
WithQuery(Match *match_clause, WithList *with_list, Return *return_clause)
|
||||
: match_clause(match_clause), with_list(with_list),
|
||||
return_clause(return_clause)
|
||||
{
|
||||
}
|
||||
|
||||
Match *match_clause;
|
||||
WithList *with_list;
|
||||
Return *return_clause;
|
||||
};
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "utils/string/file.hpp"
|
||||
|
||||
auto extract_queries(const std::vector<std::string>& arguments)
|
||||
{
|
||||
std::vector<std::string> queries;
|
||||
|
||||
// load single query
|
||||
if (contains_argument(arguments, "-q"))
|
||||
{
|
||||
queries.emplace_back(get_argument(arguments, "-q", "CREATE (n) RETURN n"));
|
||||
return queries;
|
||||
}
|
||||
|
||||
// load multiple queries from file
|
||||
auto default_file = "queries.cypher";
|
||||
auto file = get_argument(arguments, "-f", default_file);
|
||||
return utils::read_lines(file.c_str());
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "tokenizer/cypher_lexer.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace cypher
|
||||
{
|
||||
|
||||
class Compiler
|
||||
{
|
||||
public:
|
||||
Compiler() = default;
|
||||
|
||||
ast::Ast syntax_tree(const std::string& input)
|
||||
{
|
||||
auto parser = cypher::Parser();
|
||||
auto tokenizer = lexer.tokenize(input);
|
||||
auto tree = parser.parse(tokenizer);
|
||||
return tree;
|
||||
}
|
||||
|
||||
private:
|
||||
CypherLexer lexer;
|
||||
};
|
||||
|
||||
}
|
@ -1,589 +0,0 @@
|
||||
/*
|
||||
** This file contains memgraph's grammar for Cypher. Process this file
|
||||
** using the lemon parser generator to generate C code that runs
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
*/
|
||||
|
||||
%token_prefix TK_
|
||||
|
||||
%token_type {Token*}
|
||||
|
||||
%extra_argument {ast::Ast* ast}
|
||||
|
||||
%syntax_error
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int a = yy_find_shift_action(yypParser, (YYCODETYPE)i);
|
||||
if (a < YYNSTATE + YYNRULE) {
|
||||
printf("possible token: %s\n", yyTokenName[i]);
|
||||
}
|
||||
}
|
||||
throw CypherSyntaxError(TOKEN->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
%stack_overflow
|
||||
{
|
||||
throw CypherParsingError("Parser stack overflow");
|
||||
}
|
||||
|
||||
%name cypher_parser
|
||||
|
||||
%include
|
||||
{
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "query/language/cypher/token.hpp"
|
||||
#include "query/language/cypher/errors.hpp"
|
||||
#include "query/language/cypher/ast/ast.hpp"
|
||||
#include "query/language/cypher/ast/tree.hpp"
|
||||
|
||||
#define DEBUG(X) std::cout << "PARSER: " << X << std::endl
|
||||
}
|
||||
|
||||
// define operator precedence
|
||||
%left OR.
|
||||
%left AND.
|
||||
%right NOT.
|
||||
%left IN IS_NULL IS_NOT_NULL NE EQ.
|
||||
%left GT LE LT GE.
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH REM.
|
||||
|
||||
// -- start structure
|
||||
|
||||
start ::= with_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= write_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= read_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= update_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= delete_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= read_write_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
start ::= merge_query(Q). {
|
||||
ast->root = Q;
|
||||
}
|
||||
|
||||
// -- merge query
|
||||
|
||||
%type merge_query {ast::MergeQuery*}
|
||||
|
||||
merge_query(MQ) ::= merge_clause(MC) set_clause(SC) return_clause(RC). {
|
||||
MQ = ast->create<ast::MergeQuery>(MC, SC, RC);
|
||||
}
|
||||
|
||||
merge_query(MQ) ::= merge_clause(MC) return_clause(RC). {
|
||||
MQ = ast->create<ast::MergeQuery>(MC, nullptr, RC);
|
||||
}
|
||||
|
||||
// -- with query
|
||||
|
||||
%type with_query {ast::WithQuery*}
|
||||
|
||||
with_query(WQ) ::= with_start_clause(SQ) with_list(WL) return_clause(RC). {
|
||||
WQ = ast->create<ast::WithQuery>(SQ, WL, RC);
|
||||
}
|
||||
|
||||
%type with_list {ast::WithList*}
|
||||
|
||||
with_list(L) ::= WITH with_clause(WC) with_list(N). {
|
||||
L = ast->create<ast::WithList>(WC, N);
|
||||
}
|
||||
|
||||
with_list(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
// TODO: replace Match with something that has Match, Create, etc.
|
||||
%type with_start_clause {ast::Match*}
|
||||
|
||||
with_start_clause(WSC) ::= match_clause(MC). {
|
||||
WSC = MC;
|
||||
}
|
||||
|
||||
%type with_clause {ast::WithClause*}
|
||||
|
||||
with_clause(WC) ::= identifier_list(IL) with_match_clause(MC). {
|
||||
WC = ast->create<ast::WithClause>(IL, MC);
|
||||
}
|
||||
|
||||
%type with_match_clause {ast::Match*}
|
||||
|
||||
with_match_clause(WMC) ::= match_clause(M). {
|
||||
WMC = M;
|
||||
}
|
||||
|
||||
with_match_clause(WMC) ::= where_clause(WC). {
|
||||
WMC = ast->create<ast::Match>(nullptr, WC);
|
||||
}
|
||||
|
||||
// write query structure
|
||||
|
||||
%type write_query {ast::WriteQuery*}
|
||||
|
||||
write_query(WQ) ::= create_clause(C) return_clause(R). {
|
||||
WQ = ast->create<ast::WriteQuery>(C, R);
|
||||
}
|
||||
|
||||
write_query(WQ) ::= create_clause(C). {
|
||||
WQ = ast->create<ast::WriteQuery>(C, nullptr);
|
||||
}
|
||||
|
||||
// read query structure
|
||||
|
||||
%type read_query {ast::ReadQuery*}
|
||||
|
||||
read_query(RQ) ::= match_clause(M) return_clause(R). {
|
||||
RQ = ast->create<ast::ReadQuery>(M, R);
|
||||
}
|
||||
|
||||
// -- match create query
|
||||
|
||||
%type read_write_query {ast::ReadWriteQuery*}
|
||||
|
||||
read_write_query(Q) ::= match_clause(M) create_clause(C) return_clause(R). {
|
||||
Q = ast->create<ast::ReadWriteQuery>(M, C, R);
|
||||
}
|
||||
|
||||
read_write_query(Q) ::= match_clause(M) create_clause(C). {
|
||||
Q = ast->create<ast::ReadWriteQuery>(M, C, nullptr);
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
|
||||
|
||||
// update query structure
|
||||
|
||||
%type update_query {ast::UpdateQuery*}
|
||||
|
||||
update_query(UQ) ::= match_clause(M) set_clause(S) return_clause(R). {
|
||||
UQ = ast->create<ast::UpdateQuery>(M, S, R);
|
||||
}
|
||||
|
||||
update_query(UQ) ::= match_clause(M) set_clause(S). {
|
||||
UQ = ast->create<ast::UpdateQuery>(M, S, nullptr);
|
||||
}
|
||||
|
||||
// set clause
|
||||
|
||||
%type set_clause {ast::Set*}
|
||||
|
||||
set_clause(S) ::= SET set_list(L). {
|
||||
S = ast->create<ast::Set>(L);
|
||||
}
|
||||
|
||||
// delete query structure
|
||||
|
||||
%type delete_query {ast::DeleteQuery*}
|
||||
|
||||
delete_query(DQ) ::= match_clause(M) delete_clause(D). {
|
||||
DQ = ast->create<ast::DeleteQuery>(M, D);
|
||||
}
|
||||
|
||||
%type create_clause {ast::Create*}
|
||||
|
||||
create_clause(C) ::= CREATE pattern(P). {
|
||||
C = ast->create<ast::Create>(P);
|
||||
}
|
||||
|
||||
%type merge_clause {ast::Merge*}
|
||||
|
||||
merge_clause(M) ::= MERGE pattern(P). {
|
||||
M = ast->create<ast::Merge>(P);
|
||||
}
|
||||
|
||||
%type match_clause {ast::Match*}
|
||||
|
||||
match_clause(M) ::= MATCH pattern_list(P) where_clause(W). {
|
||||
M = ast->create<ast::Match>(P, W);
|
||||
}
|
||||
|
||||
%type delete_clause {ast::Delete*}
|
||||
|
||||
// TODO: expression list support
|
||||
|
||||
delete_clause(D) ::= DELETE idn(I). {
|
||||
D = ast->create<ast::Delete>(I, false);
|
||||
}
|
||||
|
||||
delete_clause(D) ::= DETACH DELETE idn(I). {
|
||||
D = ast->create<ast::Delete>(I, true);
|
||||
}
|
||||
|
||||
// ---- pattern list
|
||||
|
||||
%type pattern_list {ast::PatternList*}
|
||||
|
||||
pattern_list(L) ::= pattern(P) COMMA pattern_list(N). {
|
||||
L = ast->create<ast::PatternList>(P, N);
|
||||
}
|
||||
|
||||
pattern_list(L) ::= pattern(P). {
|
||||
L = ast->create<ast::PatternList>(P, nullptr);
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
%type pattern {ast::Pattern*}
|
||||
|
||||
pattern(P) ::= node(N) rel(R) pattern(NEXT). {
|
||||
P = ast->create<ast::Pattern>(N, R, NEXT);
|
||||
}
|
||||
|
||||
pattern(P) ::= node(N). {
|
||||
P = ast->create<ast::Pattern>(N, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// update query
|
||||
// MATCH ... WITH ... SET ... RETURN
|
||||
|
||||
%type rel {ast::Relationship*}
|
||||
|
||||
rel(R) ::= MINUS rel_spec(S) MINUS. { // bidirectional
|
||||
R = ast->create<ast::Relationship>(S, ast::Relationship::Both);
|
||||
}
|
||||
|
||||
rel(R) ::= LT MINUS rel_spec(S) MINUS. { // left
|
||||
R = ast->create<ast::Relationship>(S, ast::Relationship::Left);
|
||||
}
|
||||
|
||||
rel(R) ::= MINUS rel_spec(S) MINUS GT. { // right
|
||||
R = ast->create<ast::Relationship>(S, ast::Relationship::Right);
|
||||
}
|
||||
|
||||
%type rel_spec {ast::RelationshipSpecs*}
|
||||
|
||||
rel_spec(R) ::= LSP rel_idn(I) rel_type(T) properties(P) RSP. {
|
||||
R = ast->create<ast::RelationshipSpecs>(I, T, P);
|
||||
}
|
||||
|
||||
rel_spec(R) ::= . {
|
||||
R = nullptr;
|
||||
}
|
||||
|
||||
%type rel_idn {ast::Identifier*}
|
||||
|
||||
rel_idn(R) ::= idn(I). {
|
||||
R = I;
|
||||
}
|
||||
|
||||
rel_idn(R) ::= . {
|
||||
R = nullptr;
|
||||
}
|
||||
|
||||
%type rel_type {ast::RelationshipTypeList*}
|
||||
|
||||
rel_type(L) ::= COLON rel_list(R). {
|
||||
L = R;
|
||||
}
|
||||
|
||||
rel_type(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
%type rel_list {ast::RelationshipTypeList*}
|
||||
|
||||
rel_list(L) ::= idn(I) PIPE rel_list(R). {
|
||||
L = ast->create<ast::RelationshipTypeList>(I, R);
|
||||
}
|
||||
|
||||
rel_list(L) ::= idn(I). {
|
||||
L = ast->create<ast::RelationshipTypeList>(I, nullptr);
|
||||
}
|
||||
|
||||
%type node {ast::Node*}
|
||||
|
||||
// node specification
|
||||
node(N) ::= LP node_idn(I) label_idn(L) properties(P) RP. {
|
||||
N = ast->create<ast::Node>(I, L, P);
|
||||
}
|
||||
|
||||
%type node_idn {ast::Identifier*}
|
||||
|
||||
// a node identifier can be ommitted
|
||||
node_idn(N) ::= idn(I). {
|
||||
N = I;
|
||||
}
|
||||
|
||||
node_idn(N) ::= . {
|
||||
N = nullptr;
|
||||
}
|
||||
|
||||
%type label_idn {ast::LabelList*}
|
||||
|
||||
// a label can be ommited or there can be more of them
|
||||
label_idn(L) ::= COLON idn(I) label_idn(N). {
|
||||
L = ast->create<ast::LabelList>(I, N);
|
||||
}
|
||||
|
||||
label_idn(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
%type where_clause {ast::Where*}
|
||||
|
||||
// where clause
|
||||
where_clause(W) ::= WHERE expr(E). {
|
||||
W = ast->create<ast::Where>(E);
|
||||
}
|
||||
|
||||
where_clause(W) ::= . {
|
||||
W = nullptr;
|
||||
}
|
||||
|
||||
%type return_clause {ast::Return*}
|
||||
|
||||
return_clause(R) ::= RETURN return_list(L). {
|
||||
R = ast->create<ast::Return>(L);
|
||||
}
|
||||
|
||||
return_clause(R) ::= RETURN distinct(D). {
|
||||
R = ast->create<ast::Return>(D);
|
||||
}
|
||||
|
||||
%type return_list {ast::ReturnList*}
|
||||
|
||||
return_list(R) ::= return_list(N) COMMA expr(E). {
|
||||
R = ast->create<ast::ReturnList>(E, N);
|
||||
}
|
||||
|
||||
return_list(R) ::= expr(E). {
|
||||
R = ast->create<ast::ReturnList>(E, nullptr);
|
||||
}
|
||||
|
||||
%type distinct {ast::Distinct*}
|
||||
|
||||
distinct(R) ::= DISTINCT idn(I). {
|
||||
R = ast->create<ast::Distinct>(I);
|
||||
}
|
||||
|
||||
// list of properties
|
||||
// e.g. { name: "wayne", surname: "rooney"}
|
||||
|
||||
%type properties {ast::PropertyList*}
|
||||
|
||||
// '{' <property_list> '}'
|
||||
properties(P) ::= LCP property_list(L) RCP. {
|
||||
P = L;
|
||||
}
|
||||
|
||||
properties(P) ::= . {
|
||||
P = nullptr;
|
||||
}
|
||||
|
||||
%type property_list {ast::PropertyList*}
|
||||
|
||||
// <property> [[',' <property>]]*
|
||||
property_list(L) ::= property(P) COMMA property_list(N). {
|
||||
L = ast->create<ast::PropertyList>(P, N);
|
||||
}
|
||||
|
||||
property_list(L) ::= property(P). {
|
||||
L = ast->create<ast::PropertyList>(P, nullptr);
|
||||
}
|
||||
|
||||
%type property {ast::Property*}
|
||||
|
||||
// IDENTIFIER ':' <expression>
|
||||
property(P) ::= idn(I) COLON value_expr(E). {
|
||||
P = ast->create<ast::Property>(I, E);
|
||||
}
|
||||
|
||||
%type value_expr {ast::Expr*}
|
||||
|
||||
value_expr(E) ::= value_expr(L) AND value_expr(R). {
|
||||
E = ast->create<ast::And>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) OR value_expr(R). {
|
||||
E = ast->create<ast::Or>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) LT value_expr(R). {
|
||||
E = ast->create<ast::Lt>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) GT value_expr(R). {
|
||||
E = ast->create<ast::Gt>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) GE value_expr(R). {
|
||||
E = ast->create<ast::Ge>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) LE value_expr(R). {
|
||||
E = ast->create<ast::Le>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) EQ value_expr(R). {
|
||||
E = ast->create<ast::Eq>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) NE value_expr(R). {
|
||||
E = ast->create<ast::Ne>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) PLUS value_expr(R). {
|
||||
E = ast->create<ast::Plus>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) MINUS value_expr(R). {
|
||||
E = ast->create<ast::Minus>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) STAR value_expr(R). {
|
||||
E = ast->create<ast::Star>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) SLASH value_expr(R). {
|
||||
E = ast->create<ast::Slash>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= value_expr(L) REM value_expr(R). {
|
||||
E = ast->create<ast::Rem>(L, R);
|
||||
}
|
||||
|
||||
value_expr(E) ::= idn(I). {
|
||||
E = ast->create<ast::Accessor>(I, nullptr);
|
||||
}
|
||||
|
||||
value_expr(E) ::= idn(I) DOT idn(P). {
|
||||
E = ast->create<ast::Accessor>(I, P);
|
||||
}
|
||||
|
||||
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*}
|
||||
|
||||
idn(I) ::= IDN(X). {
|
||||
I = ast->create<ast::Identifier>(X->value);
|
||||
}
|
||||
|
||||
// ---- identifier list
|
||||
|
||||
%type identifier_list {ast::IdentifierList*}
|
||||
|
||||
identifier_list(L) ::= idn(I) COMMA identifier_list(N). {
|
||||
L = ast->create<ast::IdentifierList>(I, N);
|
||||
}
|
||||
|
||||
identifier_list(L) ::= idn(I). {
|
||||
L = ast->create<ast::IdentifierList>(I, nullptr);
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
value_expr(E) ::= LONG(V). {
|
||||
auto value = std::stol(V->value);
|
||||
E = ast->create<ast::Long>(value);
|
||||
}
|
||||
|
||||
value_expr(E) ::= FLOAT(V). {
|
||||
auto value = std::stod(V->value);
|
||||
E = ast->create<ast::Float>(value);
|
||||
}
|
||||
|
||||
value_expr(E) ::= STR(V). {
|
||||
auto value = V->value.substr(1, V->value.size() - 2);
|
||||
E = ast->create<ast::String>(value);
|
||||
}
|
||||
|
||||
value_expr(E) ::= BOOL(V). {
|
||||
auto value = V->value[0] == 't' || V->value[0] == 'T' ? true : false;
|
||||
E = ast->create<ast::Boolean>(value);
|
||||
}
|
||||
|
||||
%type pattern_expr {ast::Expr*}
|
||||
|
||||
pattern_expr(E) ::= pattern(P). {
|
||||
E = ast->create<ast::PatternExpr>(P);
|
||||
}
|
||||
|
||||
%type function_expr {ast::Expr*}
|
||||
|
||||
function_expr(E) ::= COUNT LP IDN(A) RP. {
|
||||
E = ast->create<ast::CountFunction>(A->value);
|
||||
}
|
||||
|
||||
function_expr(E) ::= LABELS LP IDN(A) RP. {
|
||||
E = ast->create<ast::LabelsFunction>(A->value);
|
||||
}
|
||||
|
||||
%type expr {ast::Expr*}
|
||||
|
||||
expr(E) ::= value_expr(V). {
|
||||
E = V;
|
||||
}
|
||||
|
||||
expr(E) ::= pattern_expr(P). {
|
||||
E = P;
|
||||
}
|
||||
|
||||
expr(E) ::= function_expr(F). {
|
||||
E = F;
|
||||
}
|
||||
|
||||
//%type alias {ast::Alias*}
|
||||
//
|
||||
//alias(A) ::= IDN(X) AS IDN(Y). {
|
||||
// A = ast->create<ast::Alias>(X->value, Y->value);
|
||||
//}
|
||||
|
||||
// set list
|
||||
// e.g. MATCH (n) SET n.name = "Ryan", n.surname = "Giggs" RETURN n
|
||||
|
||||
%type set_list {ast::SetList*}
|
||||
|
||||
set_list(L) ::= set_element(E) COMMA set_list(N). {
|
||||
L = ast->create<ast::SetList>(E, N);
|
||||
}
|
||||
|
||||
set_list(L) ::= set_element(E). {
|
||||
L = ast->create<ast::SetList>(E, nullptr);
|
||||
}
|
||||
|
||||
%type set_element {ast::SetElementBase*}
|
||||
|
||||
set_element(E) ::= accessor(A) EQ set_value(V). {
|
||||
E = ast->create<ast::SetElement>(A, V);
|
||||
}
|
||||
|
||||
set_element(E) ::= idn(I) label_idn(L). {
|
||||
E = ast->create<ast::LabelSetElement>(I, L);
|
||||
}
|
||||
|
||||
%type accessor {ast::Accessor*}
|
||||
|
||||
accessor(A) ::= idn(E) DOT idn(P). {
|
||||
A = ast->create<ast::Accessor>(E, P);
|
||||
}
|
||||
|
||||
%type set_value {ast::SetValue*}
|
||||
|
||||
set_value(V) ::= value_expr(E). {
|
||||
V = ast->create<ast::SetValue>(E);
|
||||
}
|
@ -1,443 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
|
||||
#include "query/language/cypher/visitor/traverser.hpp"
|
||||
|
||||
class PrintVisitor : public Traverser
|
||||
{
|
||||
public:
|
||||
class Printer
|
||||
{
|
||||
public:
|
||||
friend class Entry;
|
||||
|
||||
Printer(std::ostream &stream, const std::string &header)
|
||||
: stream(stream)
|
||||
{
|
||||
// stream << header;
|
||||
}
|
||||
|
||||
~Printer() { stream << std::endl; }
|
||||
|
||||
class Entry
|
||||
{
|
||||
public:
|
||||
explicit Entry(Printer &printer) : printer(printer), valid(true)
|
||||
{
|
||||
printer.level++;
|
||||
|
||||
for (size_t i = 1; i < printer.level; ++i)
|
||||
printer.stream << "| ";
|
||||
|
||||
printer.stream << "+--";
|
||||
}
|
||||
|
||||
Entry(const Entry &) = delete;
|
||||
|
||||
Entry(Entry &&other) : printer(other.printer), valid(true)
|
||||
{
|
||||
other.valid = false;
|
||||
}
|
||||
|
||||
~Entry()
|
||||
{
|
||||
if (valid) printer.level--;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Entry &operator<<(const T &item)
|
||||
{
|
||||
printer.stream << item;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Printer &printer;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
Entry advance()
|
||||
{
|
||||
stream << std::endl;
|
||||
return std::move(Entry(*this));
|
||||
}
|
||||
|
||||
Entry advance(const std::string &text)
|
||||
{
|
||||
stream << std::endl;
|
||||
auto entry = Entry(*this);
|
||||
entry << text;
|
||||
return std::move(entry);
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream &stream;
|
||||
size_t level = 0;
|
||||
};
|
||||
|
||||
explicit PrintVisitor(std::ostream &stream)
|
||||
: printer(stream, "Printing AST") {}
|
||||
|
||||
void visit(ast::Start &start) override
|
||||
{
|
||||
auto entry = printer.advance("Start");
|
||||
Traverser::visit(start);
|
||||
}
|
||||
|
||||
void visit(ast::ReadQuery &read_query) override
|
||||
{
|
||||
auto entry = printer.advance("Read Query");
|
||||
Traverser::visit(read_query);
|
||||
}
|
||||
|
||||
void visit(ast::ReadWriteQuery &query) override
|
||||
{
|
||||
auto entry = printer.advance("Read Write Query");
|
||||
Traverser::visit(query);
|
||||
}
|
||||
|
||||
void visit(ast::Match &match) override
|
||||
{
|
||||
auto entry = printer.advance("Match");
|
||||
Traverser::visit(match);
|
||||
}
|
||||
|
||||
void visit(ast::Pattern &pattern) override
|
||||
{
|
||||
auto entry = printer.advance("Pattern");
|
||||
Traverser::visit(pattern);
|
||||
}
|
||||
|
||||
void visit(ast::PatternExpr &pattern_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Pattern Expression");
|
||||
Traverser::visit(pattern_expr);
|
||||
}
|
||||
|
||||
void visit(ast::PatternList &pattern_list) override
|
||||
{
|
||||
auto entry = printer.advance("Pattern List");
|
||||
Traverser::visit(pattern_list);
|
||||
}
|
||||
|
||||
void visit(ast::Node &node) override
|
||||
{
|
||||
auto entry = printer.advance("Node");
|
||||
Traverser::visit(node);
|
||||
}
|
||||
|
||||
void visit(ast::Alias &alias) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
entry << "Alias: '" << alias.name << "' AS '" << alias.alias << "'";
|
||||
}
|
||||
|
||||
void visit(ast::Identifier &idn) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
entry << "Identifier '" << idn.name << "'";
|
||||
}
|
||||
|
||||
void visit(ast::IdentifierList &list) override
|
||||
{
|
||||
auto entry = printer.advance("Identifier List");
|
||||
Traverser::visit(list);
|
||||
}
|
||||
|
||||
void visit(ast::Return &return_clause) override
|
||||
{
|
||||
auto entry = printer.advance("Return");
|
||||
Traverser::visit(return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::Distinct &distinct) override
|
||||
{
|
||||
auto entry = printer.advance("Distinct");
|
||||
Traverser::visit(distinct);
|
||||
}
|
||||
|
||||
void visit(ast::Accessor &accessor) override
|
||||
{
|
||||
auto entry = printer.advance("Accessor");
|
||||
Traverser::visit(accessor);
|
||||
}
|
||||
|
||||
void visit(ast::Boolean &boolean) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
entry << "Boolean " << boolean.value;
|
||||
}
|
||||
|
||||
void visit(ast::Float &floating) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
entry << "Float " << floating.value;
|
||||
}
|
||||
|
||||
void visit(ast::Integer &integer) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
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();
|
||||
// entry << "ULong " << ulong.value;
|
||||
// }
|
||||
|
||||
void visit(ast::String &string) override
|
||||
{
|
||||
auto entry = printer.advance();
|
||||
entry << "String " << string.value;
|
||||
}
|
||||
|
||||
void visit(ast::InternalIdExpr &internal_id) override
|
||||
{
|
||||
auto entry = printer.advance("InternalId");
|
||||
Traverser::visit(internal_id);
|
||||
}
|
||||
|
||||
void visit(ast::Property &property) override
|
||||
{
|
||||
auto entry = printer.advance("Property");
|
||||
Traverser::visit(property);
|
||||
}
|
||||
|
||||
void visit(ast::And &and_expr) override
|
||||
{
|
||||
auto entry = printer.advance("And");
|
||||
Traverser::visit(and_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Or &or_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Or");
|
||||
Traverser::visit(or_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Lt <_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Less Than");
|
||||
Traverser::visit(lt_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Gt >_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Greater Than");
|
||||
Traverser::visit(gt_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Ge &ge_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Greater od Equal");
|
||||
Traverser::visit(ge_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Le &le_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Less or Equal");
|
||||
Traverser::visit(le_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Eq &eq_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Equal");
|
||||
Traverser::visit(eq_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Ne &ne_expr) override
|
||||
{
|
||||
auto entry = printer.advance("Not Equal");
|
||||
Traverser::visit(ne_expr);
|
||||
}
|
||||
|
||||
void visit(ast::Plus &plus) override
|
||||
{
|
||||
auto entry = printer.advance("Plus");
|
||||
Traverser::visit(plus);
|
||||
}
|
||||
|
||||
void visit(ast::Minus &minus) override
|
||||
{
|
||||
auto entry = printer.advance("Minus");
|
||||
Traverser::visit(minus);
|
||||
}
|
||||
|
||||
void visit(ast::Star &star) override
|
||||
{
|
||||
auto entry = printer.advance("Star");
|
||||
Traverser::visit(star);
|
||||
}
|
||||
|
||||
void visit(ast::Slash &slash) override
|
||||
{
|
||||
auto entry = printer.advance("Slash");
|
||||
Traverser::visit(slash);
|
||||
}
|
||||
|
||||
void visit(ast::Rem &rem) override
|
||||
{
|
||||
auto entry = printer.advance("Rem (%)");
|
||||
Traverser::visit(rem);
|
||||
}
|
||||
|
||||
void visit(ast::CountFunction &count) override
|
||||
{
|
||||
auto entry = printer.advance("Count ");
|
||||
entry << count.name << "(" << count.argument << ")";
|
||||
}
|
||||
|
||||
void visit(ast::LabelsFunction &labels) override
|
||||
{
|
||||
auto entry = printer.advance("Labels ");
|
||||
entry << labels.name << "(" << labels.argument << ")";
|
||||
}
|
||||
|
||||
void visit(ast::PropertyList &prop_list) override
|
||||
{
|
||||
auto entry = printer.advance("Property List");
|
||||
Traverser::visit(prop_list);
|
||||
}
|
||||
|
||||
void visit(ast::RelationshipTypeList &rel_list) override
|
||||
{
|
||||
auto entry = printer.advance("Relationship Type List");
|
||||
Traverser::visit(rel_list);
|
||||
}
|
||||
|
||||
void visit(ast::Relationship &rel) override
|
||||
{
|
||||
auto entry = printer.advance("Relationship");
|
||||
entry << " direction: " << rel.direction;
|
||||
Traverser::visit(rel);
|
||||
}
|
||||
|
||||
void visit(ast::RelationshipSpecs &rel_specs) override
|
||||
{
|
||||
auto entry = printer.advance("Relationship Specs");
|
||||
Traverser::visit(rel_specs);
|
||||
}
|
||||
|
||||
void visit(ast::LabelList &labels) override
|
||||
{
|
||||
auto entry = printer.advance("Label List");
|
||||
Traverser::visit(labels);
|
||||
}
|
||||
|
||||
void visit(ast::ReturnList &return_list) override
|
||||
{
|
||||
auto entry = printer.advance("Return List");
|
||||
Traverser::visit(return_list);
|
||||
}
|
||||
|
||||
void visit(ast::Where &where) override
|
||||
{
|
||||
auto entry = printer.advance("Where");
|
||||
Traverser::visit(where);
|
||||
}
|
||||
|
||||
void visit(ast::WriteQuery &write_query) override
|
||||
{
|
||||
auto entry = printer.advance("Write Query");
|
||||
Traverser::visit(write_query);
|
||||
}
|
||||
|
||||
void visit(ast::MergeQuery &merge_query) override
|
||||
{
|
||||
auto entry = printer.advance("Merge Query");
|
||||
Traverser::visit(merge_query);
|
||||
}
|
||||
|
||||
void visit(ast::DeleteQuery &delete_query) override
|
||||
{
|
||||
auto entry = printer.advance("Delete Query");
|
||||
Traverser::visit(delete_query);
|
||||
}
|
||||
|
||||
void visit(ast::Delete &delete_clause) override
|
||||
{
|
||||
auto is_detached = delete_clause.is_detached ? "yes" : "no";
|
||||
auto text_entry =
|
||||
std::string("Delete - DETACH: ") + std::string(is_detached);
|
||||
auto entry = printer.advance(text_entry);
|
||||
Traverser::visit(delete_clause);
|
||||
}
|
||||
|
||||
void visit(ast::Create &create) override
|
||||
{
|
||||
auto entry = printer.advance("Create");
|
||||
Traverser::visit(create);
|
||||
}
|
||||
|
||||
void visit(ast::Merge &merge) override
|
||||
{
|
||||
auto entry = printer.advance("Merge");
|
||||
Traverser::visit(merge);
|
||||
}
|
||||
|
||||
void visit(ast::UpdateQuery &update_query) override
|
||||
{
|
||||
auto entry = printer.advance("Update Query");
|
||||
Traverser::visit(update_query);
|
||||
}
|
||||
|
||||
void visit(ast::Set &set_clause) override
|
||||
{
|
||||
auto entry = printer.advance("Set");
|
||||
Traverser::visit(set_clause);
|
||||
}
|
||||
|
||||
void visit(ast::SetValue &set_value) override
|
||||
{
|
||||
auto entry = printer.advance("Set Value");
|
||||
Traverser::visit(set_value);
|
||||
}
|
||||
|
||||
void visit(ast::SetElement &set_element) override
|
||||
{
|
||||
auto entry = printer.advance("Set Element");
|
||||
Traverser::visit(set_element);
|
||||
}
|
||||
|
||||
void visit(ast::LabelSetElement &label_set_element) override
|
||||
{
|
||||
auto entry = printer.advance("Label Set Element");
|
||||
Traverser::visit(label_set_element);
|
||||
}
|
||||
|
||||
void visit(ast::SetList &set_list) override
|
||||
{
|
||||
auto entry = printer.advance("Set List");
|
||||
Traverser::visit(set_list);
|
||||
}
|
||||
|
||||
void visit(ast::WithClause &with_clause) override
|
||||
{
|
||||
auto entry = printer.advance("With Clause");
|
||||
Traverser::visit(with_clause);
|
||||
}
|
||||
|
||||
void visit(ast::WithList &with_list) override
|
||||
{
|
||||
auto entry = printer.advance("With List");
|
||||
Traverser::visit(with_list);
|
||||
}
|
||||
|
||||
void visit(ast::WithQuery &with_query) override
|
||||
{
|
||||
auto entry = printer.advance("With Query");
|
||||
Traverser::visit(with_query);
|
||||
}
|
||||
|
||||
private:
|
||||
Printer printer;
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "token.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
class CypherParsingError : public BasicException
|
||||
{
|
||||
public:
|
||||
CypherParsingError(const std::string &what)
|
||||
: BasicException("Cypher Parsing Error: " + what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CypherLexicalError : public BasicException
|
||||
{
|
||||
public:
|
||||
CypherLexicalError(const Token &token)
|
||||
: BasicException("Cypher Lexical Error: unrecognized token '" +
|
||||
token.value + "'.")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CypherSyntaxError : public BasicException
|
||||
{
|
||||
public:
|
||||
CypherSyntaxError(const std::string &near)
|
||||
: BasicException("Cypher Syntax Error: near '" + near + "'.")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CypherSemanticError : public BasicException
|
||||
{
|
||||
public:
|
||||
CypherSemanticError(const std::string &what)
|
||||
: BasicException("Cypher Semanic Error: " + what)
|
||||
{
|
||||
}
|
||||
};
|
@ -1,54 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "cypher_lexer.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
CypherLexer lexer;
|
||||
|
||||
auto tokenizer = lexer.tokenize("{name: 'Dominik', lastName: 'Tomicevic', age: 24 }");
|
||||
|
||||
while(true)
|
||||
{
|
||||
auto token = tokenizer.lookup();
|
||||
|
||||
if(token.id == 0)
|
||||
break;
|
||||
|
||||
std::cout << token.id << " -> " << token.value << std::endl;
|
||||
}
|
||||
|
||||
|
||||
lexertl::rules rules;
|
||||
lexertl::state_machine sm;
|
||||
|
||||
rules.push("\\s+", sm.skip());
|
||||
rules.push("MATCH", 1);
|
||||
rules.push("RETURN", 2);
|
||||
|
||||
rules.push("'(.*?)'", 4); // string literal TODO single quote escape
|
||||
rules.push("\\\"(.*?)\\\"", 4); // string literal TODO double quote escape
|
||||
rules.push("[-+]?(\\d*[.])?\\d+", 5); // number
|
||||
|
||||
rules.push("[_a-zA-Z][_a-zA-Z0-9]{0,30}", 3); // identifier
|
||||
|
||||
lexertl::generator::build(rules, sm);
|
||||
|
||||
std::string input("MATCH (user:User { name: 'Dominik', age: 24})-[has:HAS]->(item:Item) WHERE item.name = 'XPS 13', AND item.price = 14.99 RETURN user, has, item");
|
||||
lexertl::smatch results(input.begin(), input.end());
|
||||
|
||||
// Read ahead
|
||||
lexertl::lookup(sm, results);
|
||||
|
||||
while (results.id != 0)
|
||||
{
|
||||
std::cout << "Id: " << results.id << ", Token: '" <<
|
||||
results.str () << "'\n";
|
||||
lexertl::lookup(sm, results);
|
||||
}
|
||||
|
||||
std::cout << "-1 to uint64_t = " << uint64_t(-1) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <vector>
|
||||
|
||||
#include "compiler.hpp"
|
||||
#include "debug/tree_print.hpp"
|
||||
#include "utils/command_line/arguments.hpp"
|
||||
#include "cypher/common.hpp"
|
||||
#include "utils/terminate_handler.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
// * INPUT ARGUMENTS *
|
||||
// -q -> query
|
||||
// -v -> visitor
|
||||
// -f -> file
|
||||
//
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::set_terminate(&terminate_handler);
|
||||
|
||||
// arguments parsing
|
||||
auto arguments = all_arguments(argc, argv);
|
||||
|
||||
// query extraction
|
||||
auto cypher_query = extract_query(arguments);
|
||||
cout << "QUERY: " << cypher_query << endl;
|
||||
|
||||
// traversers
|
||||
auto traverser = get_argument(arguments, "-t", "code");
|
||||
auto print_traverser = Traverser::sptr(new PrintVisitor(cout));
|
||||
std::map<std::string, Traverser::sptr> traversers = {
|
||||
{"print", print_traverser}
|
||||
};
|
||||
|
||||
cypher::Compiler compiler;
|
||||
auto tree = compiler.syntax_tree(cypher_query);
|
||||
|
||||
auto t = traversers[traverser];
|
||||
tree.root->accept(*t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cypher/cypher.h"
|
||||
#include "token.hpp"
|
||||
#include "ast/tree.hpp"
|
||||
#include "tokenizer/cypher_lexer.hpp"
|
||||
#include "logging/default.hpp"
|
||||
|
||||
void* cypher_parserAlloc(void* (*allocProc)(size_t));
|
||||
void cypher_parser(void*, int, Token*, ast::Ast* ast);
|
||||
void cypher_parserFree(void*, void(*freeProc)(void*));
|
||||
|
||||
namespace cypher
|
||||
{
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser() : logger(logging::log->logger("LexicalParser"))
|
||||
{
|
||||
parser = cypher_parserAlloc(malloc);
|
||||
}
|
||||
|
||||
~Parser()
|
||||
{
|
||||
cypher_parserFree(parser, free);
|
||||
}
|
||||
|
||||
ast::Ast parse(Lexer::Tokenizer tokenizer)
|
||||
{
|
||||
auto tree = ast::Ast();
|
||||
std::list<Token> tokens;
|
||||
|
||||
do
|
||||
{
|
||||
tokens.emplace_back(tokenizer.lookup());
|
||||
auto& token = tokens.back();
|
||||
// TODO: resolve fmt error with {
|
||||
// logger.debug(token.repr());
|
||||
cypher_parser(parser, token.id, &token, &tree);
|
||||
|
||||
} while(tokens.back().id != 0);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
protected:
|
||||
Logger logger;
|
||||
|
||||
private:
|
||||
void* parser;
|
||||
};
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
struct Token
|
||||
{
|
||||
unsigned long id;
|
||||
std::string value;
|
||||
|
||||
/*
|
||||
* Token is "True" if it's id is bigger than zero. Because
|
||||
* lexer ids are all bigger than zero.
|
||||
*
|
||||
* This object could be used in while loop as a conditional element.
|
||||
* E.g.:
|
||||
* while (auto token = ...)
|
||||
* {
|
||||
* }
|
||||
*/
|
||||
explicit operator bool() const
|
||||
{
|
||||
return id > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* String representation.
|
||||
*/
|
||||
std::string repr() const
|
||||
{
|
||||
// TODO: wrap fmt format
|
||||
// return fmt::format("TOKEN id = {}, value = {}", id, value);
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* Ostream operator
|
||||
*
|
||||
* Prints token id and value in single line.
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& stream, const Token& token)
|
||||
{
|
||||
return stream << token.repr();
|
||||
}
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cypher/cypher.h"
|
||||
#include "lexer.hpp"
|
||||
|
||||
class CypherLexer : public Lexer
|
||||
{
|
||||
public:
|
||||
CypherLexer()
|
||||
{
|
||||
// whitespace
|
||||
rule("\\s+", sm->skip());
|
||||
|
||||
// special characters
|
||||
rule("\\.", TK_DOT);
|
||||
rule(",", TK_COMMA);
|
||||
rule(":", TK_COLON);
|
||||
rule("\\|", TK_PIPE);
|
||||
rule("\\{", TK_LCP);
|
||||
rule("\\}", TK_RCP);
|
||||
rule("\\(", TK_LP);
|
||||
rule("\\)", TK_RP);
|
||||
rule("\\[", TK_LSP);
|
||||
rule("\\]", TK_RSP);
|
||||
|
||||
// operators
|
||||
rule("\\+", TK_PLUS);
|
||||
rule("-", TK_MINUS);
|
||||
rule("\\*", TK_STAR);
|
||||
rule("\\/", TK_SLASH);
|
||||
rule("%", TK_REM);
|
||||
|
||||
rule(">", TK_GT);
|
||||
rule("<", TK_LT);
|
||||
rule(">=", TK_GE);
|
||||
rule("<=", TK_LE);
|
||||
rule("=", TK_EQ);
|
||||
rule("<>", TK_NE);
|
||||
|
||||
// constants
|
||||
rule("(?i:TRUE)", TK_BOOL);
|
||||
rule("(?i:FALSE)", TK_BOOL);
|
||||
|
||||
// keywords
|
||||
rule("(?i:CREATE)", TK_CREATE);
|
||||
rule("(?i:MERGE)", TK_MERGE);
|
||||
rule("(?i:MATCH)", TK_MATCH);
|
||||
rule("(?i:WHERE)", TK_WHERE);
|
||||
rule("(?i:SET)", TK_SET);
|
||||
rule("(?i:RETURN)", TK_RETURN);
|
||||
rule("(?i:DISTINCT)", TK_DISTINCT);
|
||||
rule("(?i:DETACH)", TK_DETACH);
|
||||
rule("(?i:DELETE)", TK_DELETE);
|
||||
rule("(?i:WITH)", TK_WITH);
|
||||
// TODO: here should be better regex
|
||||
// problem is that id in property list isn't ID from where
|
||||
// part
|
||||
rule("(?-i:ID)", TK_ID);
|
||||
|
||||
rule("(?i:AND)", TK_AND);
|
||||
rule("(?i:OR)", TK_OR);
|
||||
|
||||
// functions
|
||||
rule("(?i:COUNT)", TK_COUNT);
|
||||
rule("(?i:LABELS)", TK_LABELS);
|
||||
|
||||
// string literal TODO single quote escape
|
||||
rule("'(.*?)'", TK_STR);
|
||||
|
||||
// string literal TODO double quote escape
|
||||
rule("\\\"(.*?)\\\"", TK_STR);
|
||||
// ALL BELOW COMBNATIONS DON'T WORK
|
||||
// rule("(?#\\\")(.*?)(?#\\\")", TK_STR);
|
||||
// rule("[\"](.*?)[\"]", TK_STR);
|
||||
// rule("(?:\")(.*?)(?:\")", TK_STR);
|
||||
// rule("(?#:\")(.*?)(?#:\")", TK_STR);
|
||||
// rule("(?#\")(.*?)(?#\")", TK_STR);
|
||||
|
||||
// number
|
||||
rule("\\d+", TK_LONG);
|
||||
rule("\\d*[.]?\\d+", TK_FLOAT);
|
||||
|
||||
// identifier
|
||||
rule("[_a-zA-Z][_a-zA-Z0-9]{0,30}", TK_IDN);
|
||||
|
||||
build();
|
||||
}
|
||||
CypherLexer(CypherLexer &other) = delete;
|
||||
CypherLexer(CypherLexer &&other) = default;
|
||||
};
|
@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
// unfortunatelly, lexertl uses some stuff deprecated in c++11 so we get some
|
||||
// warnings during compile time, mainly for the auto_ptr
|
||||
// auto_ptr<lexertl::detail::basic_re_token<char, char> > is deprecated
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#include "lexertl/generator.hpp"
|
||||
#include "lexertl/lookup.hpp"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "query/language/cypher/errors.hpp"
|
||||
#include "query/language/cypher/token.hpp"
|
||||
|
||||
class Lexer
|
||||
{
|
||||
public:
|
||||
|
||||
// public pointer declarations
|
||||
using uptr = std::unique_ptr<Lexer>;
|
||||
using sptr = std::shared_ptr<Lexer>;
|
||||
|
||||
// constructors
|
||||
// default constructor creates unique pointers to object
|
||||
// members
|
||||
Lexer() :
|
||||
rules(std::make_unique<lexertl::rules>()),
|
||||
sm(std::make_unique<lexertl::state_machine>())
|
||||
{
|
||||
}
|
||||
// copy constructor is deleted
|
||||
Lexer(Lexer& other) = delete;
|
||||
// move constructor has default implementation
|
||||
Lexer(Lexer&& other) :
|
||||
rules(std::move(other.rules)),
|
||||
sm(std::move(other.sm))
|
||||
{
|
||||
}
|
||||
|
||||
// TODO take care of concurrnecy and moving the lexer object when
|
||||
// some Tokenizer already uses the it (right now I'm not
|
||||
// sure what is going to happen)
|
||||
// check this ASAP
|
||||
class Tokenizer
|
||||
{
|
||||
public:
|
||||
Tokenizer(const Lexer& lexer, const std::string& str)
|
||||
: lexer(lexer), results(str.begin(), str.end()) {}
|
||||
|
||||
Token lookup()
|
||||
{
|
||||
lexertl::lookup(*lexer.sm, results);
|
||||
auto token = Token {results.id, results.str()};
|
||||
|
||||
if(results.id == static_cast<decltype(results.id)>(-1))
|
||||
throw CypherLexicalError(token);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
const Lexer& lexer;
|
||||
lexertl::smatch results;
|
||||
};
|
||||
|
||||
Tokenizer tokenize(const std::string& str)
|
||||
{
|
||||
return Tokenizer(*this, str);
|
||||
}
|
||||
|
||||
void build()
|
||||
{
|
||||
lexertl::generator::build(*rules, *sm);
|
||||
}
|
||||
|
||||
void rule(const std::string& regex, uint64_t id)
|
||||
{
|
||||
rules->push(regex, id);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
using uptr_lexertl_rules = std::unique_ptr<lexertl::rules>;
|
||||
using uptr_lexertl_sm = std::unique_ptr<lexertl::state_machine>;
|
||||
|
||||
uptr_lexertl_rules rules;
|
||||
uptr_lexertl_sm sm;
|
||||
};
|
@ -1,317 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/language/cypher/ast/ast.hpp"
|
||||
#include "query/language/cypher/ast/ast_visitor.hpp"
|
||||
|
||||
class Traverser : public ast::AstVisitor
|
||||
{
|
||||
public:
|
||||
using uptr = std::unique_ptr<Traverser>;
|
||||
using sptr = std::shared_ptr<Traverser>;
|
||||
|
||||
void visit(ast::Start& start_query) override
|
||||
{
|
||||
// DELETE
|
||||
}
|
||||
|
||||
void visit(ast::DeleteQuery& delete_query) override
|
||||
{
|
||||
accept(delete_query.match);
|
||||
accept(delete_query.delete_clause);
|
||||
}
|
||||
|
||||
void visit(ast::ReadQuery& read_query) override
|
||||
{
|
||||
accept(read_query.match);
|
||||
accept(read_query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::ReadWriteQuery& query) override
|
||||
{
|
||||
accept(query.match_clause);
|
||||
accept(query.create_clause);
|
||||
accept(query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::MergeQuery& query) override
|
||||
{
|
||||
accept(query.merge_clause);
|
||||
accept(query.set_clause);
|
||||
accept(query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::Match& match) override
|
||||
{
|
||||
accept(match.pattern_list);
|
||||
accept(match.where);
|
||||
}
|
||||
|
||||
void visit(ast::Pattern& pattern) override
|
||||
{
|
||||
accept(pattern.node);
|
||||
accept(pattern.relationship);
|
||||
accept(pattern.next);
|
||||
}
|
||||
|
||||
void visit(ast::PatternExpr& pattern_expr) override
|
||||
{
|
||||
accept(pattern_expr.pattern);
|
||||
}
|
||||
|
||||
void visit(ast::Node& node) override
|
||||
{
|
||||
accept(node.idn);
|
||||
accept(node.labels);
|
||||
accept(node.props);
|
||||
}
|
||||
|
||||
void visit(ast::Return& return_clause) override
|
||||
{
|
||||
accept(return_clause.return_list);
|
||||
accept(return_clause.distinct);
|
||||
}
|
||||
|
||||
void visit(ast::Accessor& accessor) override
|
||||
{
|
||||
accept(accessor.entity);
|
||||
accept(accessor.prop);
|
||||
}
|
||||
void visit(ast::Property& property) override
|
||||
{
|
||||
accept(property.idn);
|
||||
accept(property.value);
|
||||
}
|
||||
|
||||
void visit(ast::And& and_expr) override
|
||||
{
|
||||
accept(and_expr.left);
|
||||
accept(and_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Or& or_expr) override
|
||||
{
|
||||
accept(or_expr.left);
|
||||
accept(or_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Lt& lt_expr) override
|
||||
{
|
||||
accept(lt_expr.left);
|
||||
accept(lt_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Gt& gt_expr) override
|
||||
{
|
||||
accept(gt_expr.left);
|
||||
accept(gt_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Ge& ge_expr) override
|
||||
{
|
||||
accept(ge_expr.left);
|
||||
accept(ge_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Le& le_expr) override
|
||||
{
|
||||
accept(le_expr.left);
|
||||
accept(le_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Eq& eq_expr) override
|
||||
{
|
||||
accept(eq_expr.left);
|
||||
accept(eq_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Ne& ne_expr) override
|
||||
{
|
||||
accept(ne_expr.left);
|
||||
accept(ne_expr.right);
|
||||
}
|
||||
|
||||
void visit(ast::Plus& plus) override
|
||||
{
|
||||
accept(plus.left);
|
||||
accept(plus.right);
|
||||
}
|
||||
|
||||
void visit(ast::Minus& minus) override
|
||||
{
|
||||
accept(minus.left);
|
||||
accept(minus.right);
|
||||
}
|
||||
|
||||
void visit(ast::Star& star) override
|
||||
{
|
||||
accept(star.left);
|
||||
accept(star.right);
|
||||
}
|
||||
|
||||
void visit(ast::Slash& slash) override
|
||||
{
|
||||
accept(slash.left);
|
||||
accept(slash.right);
|
||||
}
|
||||
|
||||
void visit(ast::Rem& rem) override
|
||||
{
|
||||
accept(rem.left);
|
||||
accept(rem.right);
|
||||
}
|
||||
|
||||
void visit(ast::CountFunction& count) override
|
||||
{
|
||||
}
|
||||
|
||||
void visit(ast::LabelsFunction& labels) override
|
||||
{
|
||||
}
|
||||
|
||||
void visit(ast::PropertyList& prop_list) override
|
||||
{
|
||||
accept(prop_list.value);
|
||||
accept(prop_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::PatternList& pattern_list) override
|
||||
{
|
||||
accept(pattern_list.value);
|
||||
accept(pattern_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::RelationshipTypeList& rel_list) override
|
||||
{
|
||||
accept(rel_list.value);
|
||||
accept(rel_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::IdentifierList& list) override
|
||||
{
|
||||
accept(list.value);
|
||||
accept(list.next);
|
||||
}
|
||||
|
||||
void visit(ast::Relationship& rel) override
|
||||
{
|
||||
accept(rel.specs);
|
||||
}
|
||||
|
||||
void visit(ast::RelationshipSpecs& rel_specs) override
|
||||
{
|
||||
accept(rel_specs.idn);
|
||||
accept(rel_specs.types);
|
||||
accept(rel_specs.props);
|
||||
}
|
||||
|
||||
void visit(ast::LabelList& labels) override
|
||||
{
|
||||
accept(labels.value);
|
||||
accept(labels.next);
|
||||
}
|
||||
|
||||
void visit(ast::ReturnList& return_list) override
|
||||
{
|
||||
accept(return_list.value);
|
||||
accept(return_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::Where& where) override
|
||||
{
|
||||
accept(where.expr);
|
||||
}
|
||||
|
||||
void visit(ast::WriteQuery& write_query) override
|
||||
{
|
||||
accept(write_query.create);
|
||||
accept(write_query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::UpdateQuery& update_query) override
|
||||
{
|
||||
accept(update_query.match_clause);
|
||||
accept(update_query.set_clause);
|
||||
accept(update_query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::Set& set_clause) override
|
||||
{
|
||||
accept(set_clause.set_list);
|
||||
}
|
||||
|
||||
void visit(ast::SetValue& set_value) override
|
||||
{
|
||||
accept(set_value.value);
|
||||
}
|
||||
|
||||
void visit(ast::SetElement& set_element) override
|
||||
{
|
||||
accept(set_element.accessor);
|
||||
accept(set_element.set_value);
|
||||
}
|
||||
|
||||
void visit(ast::LabelSetElement& label_set_element) override
|
||||
{
|
||||
accept(label_set_element.identifier);
|
||||
accept(label_set_element.label_list);
|
||||
}
|
||||
|
||||
void visit(ast::SetList& set_list) override
|
||||
{
|
||||
accept(set_list.value);
|
||||
accept(set_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::Create& create) override
|
||||
{
|
||||
accept(create.pattern);
|
||||
}
|
||||
|
||||
void visit(ast::Merge& merge) override
|
||||
{
|
||||
accept(merge.pattern);
|
||||
}
|
||||
|
||||
void visit(ast::Distinct& distinct) override
|
||||
{
|
||||
accept(distinct.identifier);
|
||||
}
|
||||
|
||||
void visit(ast::Delete& delete_clause) override
|
||||
{
|
||||
accept(delete_clause.identifier);
|
||||
}
|
||||
|
||||
void visit(ast::WithClause& with_clause) override
|
||||
{
|
||||
accept(with_clause.identifier_list);
|
||||
accept(with_clause.match_clause);
|
||||
}
|
||||
|
||||
void visit(ast::WithList& with_list) override
|
||||
{
|
||||
accept(with_list.value);
|
||||
accept(with_list.next);
|
||||
}
|
||||
|
||||
void visit(ast::WithQuery& with_query) override
|
||||
{
|
||||
accept(with_query.match_clause);
|
||||
accept(with_query.with_list);
|
||||
accept(with_query.return_clause);
|
||||
}
|
||||
|
||||
void visit(ast::InternalIdExpr& internal_id) override
|
||||
{
|
||||
accept(internal_id.entity);
|
||||
accept(internal_id.id);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<class T>
|
||||
void accept(T* node)
|
||||
{
|
||||
if(node != nullptr)
|
||||
node->accept(*this);
|
||||
}
|
||||
};
|
@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "logging/default.hpp"
|
||||
#include "query/exception/plan_compilation.hpp"
|
||||
#include "utils/string/join.hpp"
|
||||
#include "logging/loggable.hpp"
|
||||
|
||||
// TODO:
|
||||
// * all libraries have to be compiled in the server compile time
|
||||
// * compile command has to be generated
|
||||
|
||||
/**
|
||||
* Compiles code into shared object (.so)
|
||||
*/
|
||||
class PlanCompiler : public Loggable
|
||||
{
|
||||
public:
|
||||
PlanCompiler() : Loggable("PlanCompiler") {}
|
||||
|
||||
/**
|
||||
* Compiles in_file into out_file (.cpp -> .so)
|
||||
*
|
||||
* @param in_file C++ file that can be compiled into dynamic lib
|
||||
* @param out_file dynamic lib (on linux .so)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void compile(const std::string &in_file, const std::string &out_file)
|
||||
{
|
||||
std::string flags;
|
||||
|
||||
// TODO: sync this with cmake configuration
|
||||
#ifdef NDEBUG
|
||||
flags += " -DNDEBUG -O2";
|
||||
#endif
|
||||
#ifdef LOG_NO_TRACE
|
||||
flags += " -DLOG_NO_TRACE";
|
||||
#endif
|
||||
#ifdef LOG_NO_DEBUG
|
||||
flags += " -DLOG_NO_DEBUG";
|
||||
#endif
|
||||
#ifdef LOG_NO_INFO
|
||||
flags += " -DLOG_NO_INFO";
|
||||
#endif
|
||||
#ifdef LOG_NO_WARN
|
||||
flags += " -DLOG_NO_WARN";
|
||||
#endif
|
||||
#ifdef LOG_NO_ERROR
|
||||
flags += " -DLOG_NO_ERROR";
|
||||
#endif
|
||||
|
||||
// TODO: load from config (generate compile command)
|
||||
// generate compile command
|
||||
auto compile_command = utils::prints(
|
||||
"clang++" + flags,
|
||||
// "-std=c++1y -O2 -DNDEBUG",
|
||||
"-std=c++1y", // compile flags
|
||||
in_file, // input file
|
||||
"-o", out_file, // ouput file
|
||||
"-I./include", // include paths
|
||||
"-I../include",
|
||||
"-I../../include",
|
||||
"-I../../../include",
|
||||
"-I../libs/fmt",
|
||||
"-I../../libs/fmt",
|
||||
"-I../../../libs/fmt",
|
||||
"-L./ -L../ -L../../",
|
||||
"-lmemgraph_pic",
|
||||
"-shared -fPIC" // shared library flags
|
||||
);
|
||||
|
||||
logger.debug("compile command -> {}", compile_command);
|
||||
|
||||
// synchronous call
|
||||
auto compile_status = system(compile_command.c_str());
|
||||
|
||||
logger.debug("compile status {}", compile_status);
|
||||
|
||||
// if compilation has failed throw exception
|
||||
if (compile_status != 0) {
|
||||
logger.debug("FAIL: Query Code Compilation: {} -> {}", in_file,
|
||||
out_file);
|
||||
throw PlanCompilationException(
|
||||
"Code compilation error. Generated code is not compilable or "
|
||||
"compilation settings are wrong");
|
||||
}
|
||||
|
||||
logger.debug("SUCCESS: Query Code Compilation: {} -> {}", in_file,
|
||||
out_file);
|
||||
}
|
||||
};
|
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "communication/communication.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "query/stripped.hpp"
|
||||
|
||||
/**
|
||||
* @class PlanInterface
|
||||
*
|
||||
* @brief Pure Abstract class that is interface to query plans.
|
||||
*
|
||||
* @tparam Stream stream for results writing.
|
||||
*/
|
||||
template <typename Stream>
|
||||
class PlanInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* In derived classes this method has to be overriden in order to implement
|
||||
* concrete execution plan.
|
||||
*
|
||||
* @param db_accessor Accessor for ihe database.
|
||||
* @param args plan argument (vector of literal values from the query)
|
||||
* @param stream stream for results writing
|
||||
*
|
||||
* @return bool status after execution (success OR fail)
|
||||
*/
|
||||
virtual bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args, Stream &stream) = 0;
|
||||
|
||||
/**
|
||||
* Virtual destructor in base class.
|
||||
*/
|
||||
virtual ~PlanInterface() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines type of underlying extern C functions and library object class name.
|
||||
* (ObjectPrototype).
|
||||
*
|
||||
* @tparam underlying object depends on Stream template parameter because
|
||||
* it will send the results throughout a custom Stream object.
|
||||
*/
|
||||
template <typename Stream>
|
||||
struct QueryPlanTrait
|
||||
{
|
||||
using ObjectPrototype = PlanInterface<Stream>;
|
||||
using ProducePrototype = PlanInterface<Stream> *(*)();
|
||||
using DestructPrototype = void (*)(PlanInterface<Stream> *);
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/stripper.hpp"
|
||||
|
||||
/*
|
||||
* Query preprocessing contains:
|
||||
* * query stripping
|
||||
*
|
||||
* This class is here because conceptually process of query preprocessing
|
||||
* might have more than one step + in current version of C++ standard
|
||||
* isn't trivial to instantiate QueryStripper because of template arguments +
|
||||
* it depends on underlying lexical analyser.
|
||||
*
|
||||
* The preprocessing results are:
|
||||
* * stripped query |
|
||||
* * stripped arguments |-> StrippedQuery
|
||||
* * stripped query hash |
|
||||
*/
|
||||
class QueryPreprocessor : public Loggable
|
||||
{
|
||||
private:
|
||||
// In C++17 the ints will be removed.
|
||||
// as far as I understand in C++17 class template parameters
|
||||
// can be deduced just like function template parameters
|
||||
// TODO: once C++ 17 will be well suported by comilers
|
||||
// refactor this piece of code because it is hard to maintain.
|
||||
using QueryStripperT = QueryStripper<int, int, int, int>;
|
||||
|
||||
public:
|
||||
QueryPreprocessor() : Loggable("QueryPreprocessor"),
|
||||
stripper(make_query_stripper(TK_LONG, TK_FLOAT, TK_STR, TK_BOOL))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess the query:
|
||||
* * strip parameters
|
||||
* * calculate query hash
|
||||
*
|
||||
* @param query that is going to be stripped
|
||||
*
|
||||
* @return QueryStripped object
|
||||
*/
|
||||
auto preprocess(const std::string &query)
|
||||
{
|
||||
auto preprocessed = stripper.strip(query);
|
||||
|
||||
logger.info("stripped_query = {}", preprocessed.query);
|
||||
logger.info("query_hash = {}", preprocessed.hash);
|
||||
|
||||
return stripper.strip(query);
|
||||
}
|
||||
|
||||
private:
|
||||
QueryStripperT stripper;
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
|
||||
/*
|
||||
* StrippedQuery contains:
|
||||
* * stripped query
|
||||
* * plan arguments stripped from query
|
||||
* * hash of stripped query
|
||||
*/
|
||||
struct StrippedQuery {
|
||||
|
||||
StrippedQuery(const std::string &&query, TypedValueStore<> &&arguments, HashType hash)
|
||||
: query(std::forward<const std::string>(query)),
|
||||
arguments(std::forward<TypedValueStore<>>(arguments)), hash(hash) {}
|
||||
|
||||
/**
|
||||
* Copy constructor is deleted because we don't want to make unecessary
|
||||
* copies of this object (copying of string and vector could be expensive)
|
||||
*/
|
||||
StrippedQuery(const StrippedQuery &other) = delete;
|
||||
StrippedQuery &operator=(const StrippedQuery &other) = delete;
|
||||
|
||||
/**
|
||||
* Move is allowed operation because it is not expensive and we can
|
||||
* move the object after it was created.
|
||||
*/
|
||||
StrippedQuery(StrippedQuery &&other) = default;
|
||||
StrippedQuery &operator=(StrippedQuery &&other) = default;
|
||||
|
||||
/**
|
||||
* Stripped query
|
||||
*/
|
||||
const std::string query;
|
||||
|
||||
/**
|
||||
* Stripped arguments
|
||||
*/
|
||||
const TypedValueStore<> arguments;
|
||||
|
||||
/**
|
||||
* Hash based on stripped query.
|
||||
*/
|
||||
const HashType hash;
|
||||
};
|
@ -1,122 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "cypher/cypher.h"
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/language/cypher/tokenizer/cypher_lexer.hpp"
|
||||
#include "query/stripped.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
#include "utils/string/transform.hpp"
|
||||
#include "utils/variadic/variadic.hpp"
|
||||
|
||||
// TODO: all todos will be resolved once Antler will be integrated
|
||||
template<typename... Ts>
|
||||
class QueryStripper : public Loggable {
|
||||
public:
|
||||
QueryStripper(Ts &&... strip_types)
|
||||
: Loggable("QueryStripper"),
|
||||
strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)),
|
||||
lexer(std::make_unique<CypherLexer>()) {
|
||||
}
|
||||
|
||||
QueryStripper(QueryStripper &other) = delete;
|
||||
|
||||
QueryStripper(QueryStripper &&other)
|
||||
: Loggable("QueryStripper"), strip_types(std::move(other.strip_types)),
|
||||
lexer(std::move(other.lexer)) {
|
||||
}
|
||||
|
||||
auto strip(const std::string &query, const std::string &separator = " ") {
|
||||
// -------------------------------------------------------------------
|
||||
// TODO: write speed tests and then optimize, because this
|
||||
// function is called before every query execution !
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// TODO write this more optimal (resplace string
|
||||
// concatenation with something smarter)
|
||||
// TODO: in place substring replacement
|
||||
|
||||
auto tokenizer = lexer->tokenize(query);
|
||||
|
||||
// TMP size of supported token types
|
||||
constexpr auto size = std::tuple_size<decltype(strip_types)>::value;
|
||||
|
||||
TypedValueStore<> stripped_arguments;
|
||||
std::string stripped_query;
|
||||
stripped_query.reserve(query.size());
|
||||
|
||||
int counter = 0; // how many arguments have we processed so far
|
||||
while (auto token = tokenizer.lookup()) {
|
||||
if (_or(token.id, strip_types, std::make_index_sequence < size > {})) {
|
||||
switch (token.id) {
|
||||
case TK_LONG:
|
||||
stripped_arguments.set(counter, std::stoi(token.value));
|
||||
break;
|
||||
case TK_STR:
|
||||
// TODO: remove quotes view lexertl
|
||||
token.value.erase(0, 1);
|
||||
token.value.erase(token.value.length() - 1, 1);
|
||||
// TODO: remove
|
||||
stripped_arguments.set(counter, token.value);
|
||||
break;
|
||||
case TK_BOOL: {
|
||||
bool value = token.value[0] == 'T' || token.value[0] == 't';
|
||||
stripped_arguments.set(counter, value);
|
||||
break;
|
||||
}
|
||||
case TK_FLOAT:
|
||||
stripped_arguments.set(counter, std::stof(token.value));
|
||||
break;
|
||||
default:
|
||||
// TODO: other properties
|
||||
assert(false);
|
||||
}
|
||||
stripped_query += std::to_string(counter++) + separator;
|
||||
} else {
|
||||
// if token is keyword then lowercase because query hash
|
||||
// should be the same
|
||||
// TODO: probably we shoud do the lowercase before
|
||||
// or during the tokenization (SPEED TESTS)
|
||||
if (token.id == TK_OR || token.id == TK_AND ||
|
||||
token.id == TK_NOT || token.id == TK_WITH ||
|
||||
token.id == TK_SET || token.id == TK_CREATE ||
|
||||
token.id == TK_MERGE || token.id == TK_MATCH ||
|
||||
token.id == TK_DELETE || token.id == TK_DETACH ||
|
||||
token.id == TK_WHERE || token.id == TK_RETURN ||
|
||||
token.id == TK_DISTINCT || token.id == TK_COUNT ||
|
||||
token.id == TK_LABELS) {
|
||||
std::transform(token.value.begin(), token.value.end(),
|
||||
token.value.begin(), ::tolower);
|
||||
}
|
||||
stripped_query += token.value + separator;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: hash function should be a template parameter
|
||||
HashType hash = fnv(stripped_query);
|
||||
return StrippedQuery(std::move(stripped_query),
|
||||
std::move(stripped_arguments), hash);
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Ts...> strip_types;
|
||||
CypherLexer::uptr lexer;
|
||||
|
||||
template<typename Value, typename Tuple, std::size_t... index>
|
||||
bool _or(Value &&value, Tuple &&tuple, std::index_sequence<index...>) {
|
||||
return utils::or_vargs(std::forward<Value>(value),
|
||||
std::get<index>(std::forward<Tuple>(tuple))...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
decltype(auto) make_query_stripper(Ts &&... ts) {
|
||||
return QueryStripper<Ts...>(std::forward<Ts>(ts)...);
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "logging/default.hpp"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
// this is a nice way how to avoid multiple definition problem with
|
||||
// headers because it will create a unique namespace for each compilation unit
|
||||
// http://stackoverflow.com/questions/2727582/multiple-definition-in-header-file
|
||||
// but sometimes that might be a problem
|
||||
namespace {
|
||||
|
||||
class CodeLineFormatException : public BasicException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
std::string format(const std::string &format_str, const Args &... args) {
|
||||
return fmt::format(format_str, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::string code_line(const std::string &format_str, const Args &... args) {
|
||||
try {
|
||||
return "\t" + format(format_str, args...) + "\n";
|
||||
} catch (std::runtime_error &e) {
|
||||
throw CodeLineFormatException(std::string(e.what()) + " " + format_str);
|
||||
}
|
||||
}
|
||||
|
||||
class CoutSocket {
|
||||
public:
|
||||
CoutSocket() : logger(logging::log->logger("Cout Socket")) {}
|
||||
|
||||
int write(const std::string &str) {
|
||||
logger.info(str);
|
||||
return str.size();
|
||||
}
|
||||
|
||||
int write(const char *data, size_t len) {
|
||||
logger.info(std::string(data, len));
|
||||
return len;
|
||||
}
|
||||
|
||||
int write(const byte *data, size_t len) {
|
||||
std::stringstream ss;
|
||||
for (int i = 0; i < len; i++) {
|
||||
ss << data[i];
|
||||
}
|
||||
std::string output(ss.str());
|
||||
cout << output << endl;
|
||||
logger.info(output);
|
||||
return len;
|
||||
}
|
||||
|
||||
private:
|
||||
Logger logger;
|
||||
};
|
||||
|
||||
}
|
4
init.sh
4
init.sh
@ -20,4 +20,8 @@ cd libs
|
||||
./setup.sh
|
||||
cd ..
|
||||
|
||||
cd build
|
||||
wget http://www.antlr.org/download/antlr-4.6-complete.jar
|
||||
cd ..
|
||||
|
||||
echo "DONE"
|
||||
|
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
!.gitignore
|
||||
!setup.sh
|
||||
!cleanup.sh
|
||||
!CMakeLists.txt
|
||||
|
26
libs/CMakeLists.txt
Normal file
26
libs/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
# setup antlr
|
||||
option(WITH_LIBCXX "" OFF) # because of debian bug
|
||||
# http://stackoverflow.com/questions/37096062/get-a-basic-c-program-to-compile-using-clang-on-ubuntu-16/38385967#38385967
|
||||
add_subdirectory(antlr4/runtime/Cpp)
|
||||
|
||||
# setup google benchmark
|
||||
add_subdirectory(benchmark)
|
||||
|
||||
# setup cppitertools
|
||||
include_directories(cppitertools)
|
||||
|
||||
# setup fmt format
|
||||
# fmt uses google test but if fmt isn't top project (here it isn't) fmt tests
|
||||
# are disabled (reasonable configuration)
|
||||
add_subdirectory(fmt)
|
||||
|
||||
# setup google test
|
||||
add_subdirectory(googletest)
|
||||
|
||||
# setup yaml cpp
|
||||
# disable tests because yaml doesn't have MASTER_PROJECT flag like fmt has
|
||||
# to override an option use option :)
|
||||
option(YAML_CPP_BUILD_TOOLS "" OFF)
|
||||
add_subdirectory(yaml-cpp)
|
@ -1,43 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Catch
|
||||
git clone https://github.com/philsquared/Catch.git
|
||||
catch_tag="master"
|
||||
cd Catch
|
||||
git checkout ${catch_tag}
|
||||
# antlr
|
||||
git clone https://github.com/antlr/antlr4.git
|
||||
antlr4_tag="aacd2a2c95816d8dc1c05814051d631bfec4cf3e" # v4.6
|
||||
cd antlr4
|
||||
git checkout ${antlr4_tag}
|
||||
cd ..
|
||||
|
||||
# cppitertools
|
||||
git clone https://github.com/ryanhaining/cppitertools.git
|
||||
cd cppitertools
|
||||
cppitertools_tag="394cc4debcd037db199551546b6fbc3ea3066722" # master 7 Oct 2016
|
||||
# because last release was v0.2 and at the point when
|
||||
# the lib was added master had 104 commits more than v0.2
|
||||
git checkout ${cppitertools_tag}
|
||||
cd ..
|
||||
|
||||
# fmt
|
||||
git clone https://github.com/fmtlib/fmt.git
|
||||
fmt_tag="e5e4fb370ccf327bbdcdcd782eb3e53580e11094"
|
||||
fmt_tag="e5e4fb370ccf327bbdcdcd782eb3e53580e11094" # v3.0.0
|
||||
cd fmt
|
||||
git checkout ${fmt_tag}
|
||||
cmake .
|
||||
make
|
||||
cd ..
|
||||
|
||||
# google benchmark
|
||||
git clone https://github.com/google/benchmark.git
|
||||
benchmark_tag="4f8bfeae470950ef005327973f15b0044eceaceb" # v1.1.0
|
||||
cd benchmark
|
||||
git checkout ${benchmark_tag}
|
||||
cd ..
|
||||
|
||||
# google test
|
||||
git clone https://github.com/google/googletest.git
|
||||
googletest_tag="ec44c6c1675c25b9827aacd08c02433cccde7780" # v1.8.0
|
||||
cd googletest
|
||||
git checkout ${googletest_tag}
|
||||
cd ..
|
||||
|
||||
# yaml-cpp
|
||||
git clone https://github.com/jbeder/yaml-cpp
|
||||
yaml_cpp_tag="519d33fea3fbcbe7e1f89f97ee0fa539cec33eb7"
|
||||
yaml_cpp_tag="519d33fea3fbcbe7e1f89f97ee0fa539cec33eb7" # master 18 Aug 2016
|
||||
# because a bug with link process had been fixed somewhen between
|
||||
# this commit and v0.5.3
|
||||
cd yaml-cpp
|
||||
git checkout ${yaml_cpp_tag}
|
||||
cmake .
|
||||
make
|
||||
cd ..
|
||||
|
||||
# lemon
|
||||
mkdir lemon
|
||||
cd lemon
|
||||
lemon_tag="09a96bed19955697a5e20c49ad863ec2005815a2"
|
||||
wget http://www.sqlite.org/src/raw/tool/lemon.c?name=${lemon_tag} -O lemon.c
|
||||
lempar_tag="8c4e9d8517e50da391f1d89a519e743dd4afbc09"
|
||||
wget http://www.sqlite.org/src/raw/tool/lempar.c?name=${lempar_tag} -O lempar.c
|
||||
clang lemon.c -o lemon -O2
|
||||
cd ..
|
||||
|
||||
# lexertl
|
||||
git clone https://github.com/BenHanson/lexertl.git
|
||||
lexertl_tag=7d4d36a357027df0e817453cc9cf948f71047ca9
|
||||
cd lexertl
|
||||
git checkout ${lexertl_tag}
|
||||
cd ..
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "communication/bolt/v1/states/state.hpp"
|
||||
#include "communication/bolt/v1/transport/bolt_decoder.hpp"
|
||||
#include "communication/bolt/v1/transport/bolt_encoder.hpp"
|
||||
#include "communication/communication.hpp"
|
||||
#include "communication/bolt/communication.hpp"
|
||||
|
||||
#include "logging/default.hpp"
|
||||
|
@ -32,14 +32,6 @@ State *Executor::run(Session &session)
|
||||
return this->run(session, q);
|
||||
// TODO: RETURN success MAYBE
|
||||
}
|
||||
catch (const CypherLexicalError &e)
|
||||
{
|
||||
session.output_stream.write_failure(
|
||||
{{"code", "Memgraph.CypherLexicalError"},
|
||||
{"message", e.what()}});
|
||||
session.output_stream.send();
|
||||
return session.bolt.states.error.get();
|
||||
}
|
||||
catch (const QueryEngineException &e)
|
||||
{
|
||||
session.output_stream.write_failure(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user