Compare commits
25 Commits
fix_finali
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
974e0d388d | ||
|
|
fd36263b07 | ||
|
|
3d02f29bc3 | ||
|
|
7b7ce25608 | ||
|
|
936dbbd34e | ||
|
|
2acd5d80af | ||
|
|
702144bf09 | ||
|
|
50872cf893 | ||
|
|
6fad1cc43e | ||
|
|
913bfb832b | ||
|
|
851a0fcc36 | ||
|
|
84e5d70e2c | ||
|
|
dd853ded31 | ||
|
|
7cd8e1c079 | ||
|
|
d6e4f1e3b6 | ||
|
|
d942934746 | ||
|
|
b5018b364b | ||
|
|
1cd26f40a2 | ||
|
|
e3f43f0212 | ||
|
|
750098548f | ||
|
|
b4435b801a | ||
|
|
90ab54c35b | ||
|
|
82883d90b6 | ||
|
|
91afe2910c | ||
|
|
4024fc96df |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v0.15.2
|
||||||
|
|
||||||
|
### Bug Fixes and Other Changes
|
||||||
|
|
||||||
|
* Fix memory management issue that could cause OOM
|
||||||
|
|
||||||
|
## v0.15.1
|
||||||
|
|
||||||
|
### Bug Fixes and Other Changes
|
||||||
|
|
||||||
|
* Fix several memory management bugs
|
||||||
|
* Reduce memory usage in query execution
|
||||||
|
* Fix bug that crashes the database when `EXPLAIN` is used
|
||||||
|
|
||||||
## v0.15.0
|
## v0.15.0
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ string(STRIP ${COMMIT_HASH} COMMIT_HASH)
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
project(memgraph VERSION 0.15.0)
|
project(memgraph VERSION 0.15.2)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
# setup CMake module path, defines path for include() and find_package()
|
# setup CMake module path, defines path for include() and find_package()
|
||||||
@@ -71,7 +71,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
|
||||||
-Werror=switch -Werror=switch-bool -Werror=return-type")
|
-Werror=switch -Werror=switch-bool -Werror=return-type \
|
||||||
|
-Werror=return-stack-address")
|
||||||
|
|
||||||
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
|
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
- name: Binaries
|
- name: Binaries
|
||||||
archive:
|
archive:
|
||||||
- build_debug/memgraph
|
- build_debug/memgraph
|
||||||
- build_debug/memgraph_distributed
|
|
||||||
- build_release/memgraph
|
- build_release/memgraph
|
||||||
- build_release/memgraph_distributed
|
|
||||||
- build_release/tools/src/mg_client
|
- build_release/tools/src/mg_client
|
||||||
- build_release/tools/src/mg_import_csv
|
- build_release/tools/src/mg_import_csv
|
||||||
- build_release/tools/src/mg_statsd
|
|
||||||
- config
|
- config
|
||||||
filename: binaries.tar.gz
|
filename: binaries.tar.gz
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,8 @@
|
|||||||
mkdir build_release
|
mkdir build_release
|
||||||
cd build_release
|
cd build_release
|
||||||
cmake -DCMAKE_BUILD_TYPE=release ..
|
cmake -DCMAKE_BUILD_TYPE=release ..
|
||||||
TIMEOUT=1200 make -j$THREADS memgraph memgraph_distributed memgraph_ha tools memgraph__macro_benchmark memgraph__stress memgraph__manual__card_fraud_generate_snapshot memgraph__feature_benchmark__kafka__benchmark memgraph__feature_benchmark__ha__read__benchmark memgraph__feature_benchmark__ha__write__benchmark
|
TIMEOUT=1200 make -j$THREADS memgraph tools memgraph__macro_benchmark memgraph__stress
|
||||||
|
cd ..
|
||||||
# Generate distributed card fraud dataset.
|
|
||||||
cd ../tests/distributed/card_fraud
|
|
||||||
./generate_dataset.sh
|
|
||||||
cd ../../..
|
|
||||||
|
|
||||||
# Checkout to parent commit and initialize.
|
# Checkout to parent commit and initialize.
|
||||||
cd ../parent
|
cd ../parent
|
||||||
@@ -96,7 +92,3 @@
|
|||||||
mkdir output
|
mkdir output
|
||||||
cd output
|
cd output
|
||||||
cpack -G DEB --config ../CPackConfig.cmake
|
cpack -G DEB --config ../CPackConfig.cmake
|
||||||
|
|
||||||
# Generate distributed card fraud dataset.
|
|
||||||
cd ../../tests/distributed/card_fraud
|
|
||||||
./generate_dataset.sh
|
|
||||||
|
|||||||
@@ -205,22 +205,6 @@ import_external_library(rocksdb STATIC
|
|||||||
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
||||||
BUILD_COMMAND $(MAKE) rocksdb)
|
BUILD_COMMAND $(MAKE) rocksdb)
|
||||||
|
|
||||||
# Setup librdkafka.
|
|
||||||
import_external_library(librdkafka STATIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/librdkafka/lib/librdkafka.a
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/librdkafka/include/librdkafka
|
|
||||||
CMAKE_ARGS -DRDKAFKA_BUILD_STATIC=ON
|
|
||||||
-DRDKAFKA_BUILD_EXAMPLES=OFF
|
|
||||||
-DRDKAFKA_BUILD_TESTS=OFF
|
|
||||||
-DCMAKE_INSTALL_LIBDIR=lib
|
|
||||||
-DWITH_SSL=ON
|
|
||||||
# If we want SASL, we need to install it on build machines
|
|
||||||
-DWITH_SASL=OFF)
|
|
||||||
|
|
||||||
import_library(librdkafka++ STATIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/librdkafka/lib/librdkafka++.a
|
|
||||||
librdkafka-proj)
|
|
||||||
|
|
||||||
# Setup libbcrypt
|
# Setup libbcrypt
|
||||||
import_external_library(libbcrypt STATIC
|
import_external_library(libbcrypt STATIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/bcrypt.a
|
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/bcrypt.a
|
||||||
@@ -230,11 +214,3 @@ import_external_library(libbcrypt STATIC
|
|||||||
CC=${CMAKE_C_COMPILER}
|
CC=${CMAKE_C_COMPILER}
|
||||||
CXX=${CMAKE_CXX_COMPILER}
|
CXX=${CMAKE_CXX_COMPILER}
|
||||||
INSTALL_COMMAND true)
|
INSTALL_COMMAND true)
|
||||||
|
|
||||||
# Setup mgclient
|
|
||||||
import_external_library(mgclient STATIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mgclient/lib/libmgclient.a
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mgclient/include
|
|
||||||
CMAKE_ARGS -DBUILD_TESTING=OFF)
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
target_link_libraries(mgclient INTERFACE ${OPENSSL_LIBRARIES})
|
|
||||||
|
|||||||
@@ -135,13 +135,3 @@ clone git://deps.memgraph.io/rocksdb.git rocksdb $rocksdb_tag
|
|||||||
sed -i 's/-Wshadow/-Wno-defaulted-function-deleted/' rocksdb/CMakeLists.txt
|
sed -i 's/-Wshadow/-Wno-defaulted-function-deleted/' rocksdb/CMakeLists.txt
|
||||||
# remove shared library from install dependencies
|
# remove shared library from install dependencies
|
||||||
sed -i 's/TARGETS ${ROCKSDB_SHARED_LIB}/TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL/' rocksdb/CMakeLists.txt
|
sed -i 's/TARGETS ${ROCKSDB_SHARED_LIB}/TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL/' rocksdb/CMakeLists.txt
|
||||||
|
|
||||||
# kafka
|
|
||||||
kafka_tag="c319b4e987d0bc4fe4f01cf91419d90b62061655" # Mar 8, 2018
|
|
||||||
# git clone https://github.com/edenhill/librdkafka.git
|
|
||||||
clone git://deps.memgraph.io/librdkafka.git librdkafka $kafka_tag
|
|
||||||
|
|
||||||
# mgclient
|
|
||||||
mgclient_tag="fe94b3631385ef5dbe40a3d8458860dbcc33e6ea" # May 27, 2019
|
|
||||||
# git clone https://github.com/memgraph/mgclient.git
|
|
||||||
clone git://deps.memgraph.io/mgclient.git mgclient $mgclient_tag
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ optdepends=()
|
|||||||
provides=()
|
provides=()
|
||||||
conflicts=()
|
conflicts=()
|
||||||
replaces=()
|
replaces=()
|
||||||
backup=("etc/memgraph/memgraph.conf" "etc/logrotate.d/memgraph" "etc/logrotate.d/memgraph_audit")
|
backup=("etc/memgraph/memgraph.conf" "etc/logrotate.d/memgraph")
|
||||||
options=()
|
options=()
|
||||||
install=memgraph.install
|
install=memgraph.install
|
||||||
changelog=
|
changelog=
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
/etc/memgraph/memgraph.conf
|
/etc/memgraph/memgraph.conf
|
||||||
/etc/logrotate.d/memgraph
|
/etc/logrotate.d/memgraph
|
||||||
/etc/logrotate.d/memgraph_audit
|
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ chattr -i -R /usr/share/memgraph/examples || true
|
|||||||
# uses plain %config.
|
# uses plain %config.
|
||||||
%config(noreplace) "/etc/memgraph/memgraph.conf"
|
%config(noreplace) "/etc/memgraph/memgraph.conf"
|
||||||
%config(noreplace) "/etc/logrotate.d/memgraph"
|
%config(noreplace) "/etc/logrotate.d/memgraph"
|
||||||
%config(noreplace) "/etc/logrotate.d/memgraph_audit"
|
|
||||||
|
|
||||||
@CPACK_RPM_USER_INSTALL_FILES@
|
@CPACK_RPM_USER_INSTALL_FILES@
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,9 @@
|
|||||||
add_subdirectory(lisp)
|
add_subdirectory(lisp)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(requests)
|
add_subdirectory(requests)
|
||||||
add_subdirectory(integrations)
|
|
||||||
add_subdirectory(io)
|
add_subdirectory(io)
|
||||||
add_subdirectory(telemetry)
|
add_subdirectory(telemetry)
|
||||||
add_subdirectory(communication)
|
add_subdirectory(communication)
|
||||||
add_subdirectory(stats)
|
|
||||||
add_subdirectory(auth)
|
|
||||||
add_subdirectory(slk)
|
|
||||||
add_subdirectory(storage/v2)
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Common LCP files
|
# Common LCP files
|
||||||
@@ -34,10 +29,8 @@ add_custom_target(generate_lcp_common DEPENDS ${generated_lcp_common_files})
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
set(mg_single_node_sources
|
set(mg_single_node_sources
|
||||||
${lcp_common_cpp_files}
|
${lcp_common_cpp_files}
|
||||||
audit/log.cpp
|
|
||||||
data_structures/concurrent/skiplist_gc.cpp
|
data_structures/concurrent/skiplist_gc.cpp
|
||||||
database/single_node/config.cpp
|
database/single_node/config.cpp
|
||||||
database/single_node/dump.cpp
|
|
||||||
database/single_node/graph_db.cpp
|
database/single_node/graph_db.cpp
|
||||||
database/single_node/graph_db_accessor.cpp
|
database/single_node/graph_db_accessor.cpp
|
||||||
durability/single_node/state_delta.cpp
|
durability/single_node/state_delta.cpp
|
||||||
@@ -45,13 +38,11 @@ set(mg_single_node_sources
|
|||||||
durability/single_node/recovery.cpp
|
durability/single_node/recovery.cpp
|
||||||
durability/single_node/snapshooter.cpp
|
durability/single_node/snapshooter.cpp
|
||||||
durability/single_node/wal.cpp
|
durability/single_node/wal.cpp
|
||||||
glue/auth.cpp
|
|
||||||
glue/communication.cpp
|
glue/communication.cpp
|
||||||
query/common.cpp
|
query/common.cpp
|
||||||
query/frontend/ast/cypher_main_visitor.cpp
|
query/frontend/ast/cypher_main_visitor.cpp
|
||||||
query/frontend/ast/pretty_print.cpp
|
query/frontend/ast/pretty_print.cpp
|
||||||
query/frontend/parsing.cpp
|
query/frontend/parsing.cpp
|
||||||
query/frontend/semantic/required_privileges.cpp
|
|
||||||
query/frontend/semantic/symbol_generator.cpp
|
query/frontend/semantic/symbol_generator.cpp
|
||||||
query/frontend/stripped.cpp
|
query/frontend/stripped.cpp
|
||||||
query/interpret/awesome_memgraph_functions.cpp
|
query/interpret/awesome_memgraph_functions.cpp
|
||||||
@@ -86,8 +77,6 @@ add_custom_target(generate_lcp_single_node DEPENDS generate_lcp_common ${generat
|
|||||||
set(MG_SINGLE_NODE_LIBS stdc++fs Threads::Threads fmt cppitertools
|
set(MG_SINGLE_NODE_LIBS stdc++fs Threads::Threads fmt cppitertools
|
||||||
antlr_opencypher_parser_lib dl glog gflags
|
antlr_opencypher_parser_lib dl glog gflags
|
||||||
mg-utils mg-io mg-requests mg-communication)
|
mg-utils mg-io mg-requests mg-communication)
|
||||||
# These are enterprise subsystems
|
|
||||||
set(MG_SINGLE_NODE_LIBS ${MG_SINGLE_NODE_LIBS} mg-integrations-kafka mg-auth)
|
|
||||||
|
|
||||||
if (USE_LTALLOC)
|
if (USE_LTALLOC)
|
||||||
list(APPEND MG_SINGLE_NODE_LIBS ltalloc)
|
list(APPEND MG_SINGLE_NODE_LIBS ltalloc)
|
||||||
@@ -109,224 +98,8 @@ target_compile_definitions(mg-single-node PUBLIC MG_SINGLE_NODE)
|
|||||||
# END Memgraph Single Node
|
# END Memgraph Single Node
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Memgraph Distributed
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
set(mg_distributed_sources
|
|
||||||
${lcp_common_cpp_files}
|
|
||||||
audit/log.cpp
|
|
||||||
database/distributed/distributed_graph_db.cpp
|
|
||||||
distributed/bfs_rpc_clients.cpp
|
|
||||||
distributed/bfs_subcursor.cpp
|
|
||||||
distributed/cluster_discovery_master.cpp
|
|
||||||
distributed/cluster_discovery_worker.cpp
|
|
||||||
distributed/coordination.cpp
|
|
||||||
distributed/coordination_master.cpp
|
|
||||||
distributed/coordination_worker.cpp
|
|
||||||
distributed/data_manager.cpp
|
|
||||||
distributed/data_rpc_clients.cpp
|
|
||||||
distributed/data_rpc_server.cpp
|
|
||||||
distributed/dgp/partitioner.cpp
|
|
||||||
distributed/dgp/vertex_migrator.cpp
|
|
||||||
distributed/durability_rpc_master.cpp
|
|
||||||
distributed/durability_rpc_worker.cpp
|
|
||||||
distributed/dynamic_worker.cpp
|
|
||||||
distributed/index_rpc_server.cpp
|
|
||||||
distributed/plan_consumer.cpp
|
|
||||||
distributed/plan_dispatcher.cpp
|
|
||||||
distributed/produce_rpc_server.cpp
|
|
||||||
distributed/pull_rpc_clients.cpp
|
|
||||||
distributed/updates_rpc_clients.cpp
|
|
||||||
distributed/updates_rpc_server.cpp
|
|
||||||
query/distributed/interpreter.cpp
|
|
||||||
query/distributed/plan/planner.cpp
|
|
||||||
query/distributed/plan/ops.cpp
|
|
||||||
query/distributed/plan/pretty_print.cpp
|
|
||||||
storage/distributed/concurrent_id_mapper_master.cpp
|
|
||||||
storage/distributed/concurrent_id_mapper_worker.cpp
|
|
||||||
transactions/distributed/engine_master.cpp
|
|
||||||
transactions/distributed/engine_worker.cpp
|
|
||||||
data_structures/concurrent/skiplist_gc.cpp
|
|
||||||
database/distributed/config.cpp
|
|
||||||
database/distributed/graph_db_accessor.cpp
|
|
||||||
durability/distributed/state_delta.cpp
|
|
||||||
durability/distributed/paths.cpp
|
|
||||||
durability/distributed/recovery.cpp
|
|
||||||
durability/distributed/snapshooter.cpp
|
|
||||||
durability/distributed/wal.cpp
|
|
||||||
glue/auth.cpp
|
|
||||||
glue/communication.cpp
|
|
||||||
query/common.cpp
|
|
||||||
query/frontend/ast/pretty_print.cpp
|
|
||||||
query/frontend/ast/cypher_main_visitor.cpp
|
|
||||||
query/frontend/parsing.cpp
|
|
||||||
query/frontend/semantic/required_privileges.cpp
|
|
||||||
query/frontend/semantic/symbol_generator.cpp
|
|
||||||
query/frontend/stripped.cpp
|
|
||||||
query/interpret/awesome_memgraph_functions.cpp
|
|
||||||
query/interpreter.cpp
|
|
||||||
query/plan/operator.cpp
|
|
||||||
query/plan/preprocess.cpp
|
|
||||||
query/plan/pretty_print.cpp
|
|
||||||
query/plan/profile.cpp
|
|
||||||
query/plan/rewrite/index_lookup.cpp
|
|
||||||
query/plan/rule_based_planner.cpp
|
|
||||||
query/plan/variable_start_planner.cpp
|
|
||||||
query/repl.cpp
|
|
||||||
query/distributed/serialization.cpp
|
|
||||||
query/typed_value.cpp
|
|
||||||
storage/common/locking/record_lock.cpp
|
|
||||||
storage/common/types/property_value.cpp
|
|
||||||
storage/common/types/property_value_store.cpp
|
|
||||||
storage/common/types/slk.cpp
|
|
||||||
storage/distributed/edge_accessor.cpp
|
|
||||||
storage/distributed/edges_iterator.cpp
|
|
||||||
storage/distributed/record_accessor.cpp
|
|
||||||
storage/distributed/rpc/serialization.cpp
|
|
||||||
storage/distributed/vertex_accessor.cpp
|
|
||||||
memgraph_init.cpp
|
|
||||||
transactions/distributed/engine_single_node.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
define_add_lcp(add_lcp_distributed mg_distributed_sources generated_lcp_distributed_files)
|
|
||||||
|
|
||||||
add_lcp_distributed(durability/distributed/state_delta.lcp)
|
|
||||||
add_lcp_distributed(database/distributed/serialization.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS durability/distributed/state_delta.lcp)
|
|
||||||
add_lcp_distributed(distributed/bfs_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/coordination_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/data_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/durability_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/index_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/plan_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/pull_produce_rpc_messages.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS transactions/distributed/serialization.lcp)
|
|
||||||
add_lcp_distributed(distributed/storage_gc_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/token_sharing_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/updates_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(distributed/dynamic_worker_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
|
|
||||||
add_lcp_distributed(query/distributed/frontend/ast/ast_serialization.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS query/frontend/ast/ast.lcp)
|
|
||||||
add_lcp_distributed(query/distributed/frontend/semantic/symbol_serialization.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS query/frontend/semantic/symbol.lcp)
|
|
||||||
add_lcp_distributed(query/distributed/plan/ops.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS query/plan/operator.lcp)
|
|
||||||
|
|
||||||
add_lcp_distributed(storage/distributed/rpc/concurrent_id_mapper_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_distributed(transactions/distributed/engine_rpc_messages.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS transactions/distributed/serialization.lcp)
|
|
||||||
|
|
||||||
add_custom_target(generate_lcp_distributed DEPENDS generate_lcp_common ${generated_lcp_distributed_files})
|
|
||||||
|
|
||||||
set(MG_DISTRIBUTED_LIBS stdc++fs Threads::Threads fmt cppitertools
|
|
||||||
antlr_opencypher_parser_lib dl glog gflags
|
|
||||||
mg-utils mg-io mg-integrations-kafka mg-requests
|
|
||||||
mg-communication mg-comm-rpc mg-auth)
|
|
||||||
|
|
||||||
# STATIC library used by memgraph executables
|
|
||||||
add_library(mg-distributed STATIC ${mg_distributed_sources})
|
|
||||||
target_link_libraries(mg-distributed ${MG_DISTRIBUTED_LIBS})
|
|
||||||
add_dependencies(mg-distributed generate_opencypher_parser)
|
|
||||||
add_dependencies(mg-distributed generate_lcp_distributed)
|
|
||||||
target_compile_definitions(mg-distributed PUBLIC MG_DISTRIBUTED)
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# END Memgraph Distributed
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Memgraph Single Node High Availability
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
set(mg_single_node_ha_sources
|
|
||||||
${lcp_common_cpp_files}
|
|
||||||
audit/log.cpp
|
|
||||||
data_structures/concurrent/skiplist_gc.cpp
|
|
||||||
database/single_node_ha/config.cpp
|
|
||||||
database/single_node_ha/graph_db.cpp
|
|
||||||
database/single_node_ha/graph_db_accessor.cpp
|
|
||||||
durability/single_node_ha/state_delta.cpp
|
|
||||||
durability/single_node_ha/paths.cpp
|
|
||||||
durability/single_node_ha/snapshooter.cpp
|
|
||||||
durability/single_node_ha/recovery.cpp
|
|
||||||
glue/auth.cpp
|
|
||||||
glue/communication.cpp
|
|
||||||
raft/coordination.cpp
|
|
||||||
raft/raft_server.cpp
|
|
||||||
raft/storage_info.cpp
|
|
||||||
query/common.cpp
|
|
||||||
query/frontend/ast/cypher_main_visitor.cpp
|
|
||||||
query/frontend/ast/pretty_print.cpp
|
|
||||||
query/frontend/parsing.cpp
|
|
||||||
query/frontend/semantic/required_privileges.cpp
|
|
||||||
query/frontend/semantic/symbol_generator.cpp
|
|
||||||
query/frontend/stripped.cpp
|
|
||||||
query/interpret/awesome_memgraph_functions.cpp
|
|
||||||
query/interpreter.cpp
|
|
||||||
query/plan/operator.cpp
|
|
||||||
query/plan/preprocess.cpp
|
|
||||||
query/plan/pretty_print.cpp
|
|
||||||
query/plan/profile.cpp
|
|
||||||
query/plan/rewrite/index_lookup.cpp
|
|
||||||
query/plan/rule_based_planner.cpp
|
|
||||||
query/plan/variable_start_planner.cpp
|
|
||||||
query/repl.cpp
|
|
||||||
query/typed_value.cpp
|
|
||||||
storage/common/constraints/record.cpp
|
|
||||||
storage/common/constraints/unique_constraints.cpp
|
|
||||||
storage/common/types/property_value.cpp
|
|
||||||
storage/common/types/slk.cpp
|
|
||||||
storage/common/types/property_value_store.cpp
|
|
||||||
storage/common/locking/record_lock.cpp
|
|
||||||
storage/single_node_ha/edge_accessor.cpp
|
|
||||||
storage/single_node_ha/record_accessor.cpp
|
|
||||||
storage/single_node_ha/vertex_accessor.cpp
|
|
||||||
transactions/single_node_ha/engine.cpp
|
|
||||||
memgraph_init.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
define_add_lcp(add_lcp_single_node_ha mg_single_node_ha_sources generated_lcp_single_node_ha_files)
|
|
||||||
|
|
||||||
add_lcp_single_node_ha(durability/single_node_ha/state_delta.lcp)
|
|
||||||
add_lcp_single_node_ha(database/single_node_ha/serialization.lcp SLK_SERIALIZE
|
|
||||||
DEPENDS durability/single_node_ha/state_delta.lcp)
|
|
||||||
add_lcp_single_node_ha(raft/raft_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_single_node_ha(raft/log_entry.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_single_node_ha(raft/snapshot_metadata.lcp SLK_SERIALIZE)
|
|
||||||
add_lcp_single_node_ha(raft/storage_info_rpc_messages.lcp SLK_SERIALIZE)
|
|
||||||
|
|
||||||
add_custom_target(generate_lcp_single_node_ha DEPENDS generate_lcp_common ${generated_lcp_single_node_ha_files})
|
|
||||||
|
|
||||||
set(MG_SINGLE_NODE_HA_LIBS stdc++fs Threads::Threads fmt cppitertools
|
|
||||||
antlr_opencypher_parser_lib dl glog gflags
|
|
||||||
mg-utils mg-io mg-integrations-kafka mg-requests mg-communication mg-comm-rpc
|
|
||||||
mg-auth)
|
|
||||||
|
|
||||||
if (USE_LTALLOC)
|
|
||||||
list(APPEND MG_SINGLE_NODE_HA_LIBS ltalloc)
|
|
||||||
# TODO(mferencevic): Enable this when clang is updated on apollo.
|
|
||||||
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (READLINE_FOUND)
|
|
||||||
list(APPEND MG_SINGLE_NODE_HA_LIBS readline)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(mg-single-node-ha STATIC ${mg_single_node_ha_sources})
|
|
||||||
target_link_libraries(mg-single-node-ha ${MG_SINGLE_NODE_HA_LIBS})
|
|
||||||
add_dependencies(mg-single-node-ha generate_opencypher_parser)
|
|
||||||
add_dependencies(mg-single-node-ha generate_lcp_single_node_ha)
|
|
||||||
target_compile_definitions(mg-single-node-ha PUBLIC MG_SINGLE_NODE_HA)
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# END Memgraph Single Node High Availability
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
add_custom_target(generate_lcp)
|
add_custom_target(generate_lcp)
|
||||||
add_dependencies(generate_lcp generate_lcp_single_node generate_lcp_single_node_ha generate_lcp_distributed)
|
add_dependencies(generate_lcp generate_lcp_single_node)
|
||||||
|
|
||||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||||
|
|
||||||
@@ -382,8 +155,6 @@ install(FILES ${CMAKE_SOURCE_DIR}/config/community.conf
|
|||||||
# Install logrotate configuration (must use absolute path).
|
# Install logrotate configuration (must use absolute path).
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate.conf
|
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate.conf
|
||||||
DESTINATION /etc/logrotate.d RENAME memgraph)
|
DESTINATION /etc/logrotate.d RENAME memgraph)
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate_audit.conf
|
|
||||||
DESTINATION /etc/logrotate.d RENAME memgraph_audit)
|
|
||||||
# Create empty directories for default location of lib and log.
|
# Create empty directories for default location of lib and log.
|
||||||
install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}/var/log/memgraph
|
install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}/var/log/memgraph
|
||||||
\$ENV{DESTDIR}/var/lib/memgraph)")
|
\$ENV{DESTDIR}/var/lib/memgraph)")
|
||||||
@@ -403,32 +174,3 @@ install(
|
|||||||
${CMAKE_BINARY_DIR}/tests/manual/bolt_client
|
${CMAKE_BINARY_DIR}/tests/manual/bolt_client
|
||||||
WORKING_DIRECTORY ${examples})")
|
WORKING_DIRECTORY ${examples})")
|
||||||
install(DIRECTORY ${examples}/build/ DESTINATION share/memgraph/examples)
|
install(DIRECTORY ${examples}/build/ DESTINATION share/memgraph/examples)
|
||||||
|
|
||||||
|
|
||||||
# memgraph distributed main executable
|
|
||||||
add_executable(memgraph_distributed memgraph_distributed.cpp)
|
|
||||||
target_link_libraries(memgraph_distributed mg-distributed kvstore_lib telemetry_lib)
|
|
||||||
set_target_properties(memgraph_distributed PROPERTIES
|
|
||||||
# Set the executable output name to include version information.
|
|
||||||
OUTPUT_NAME "memgraph_distributed-${memgraph_VERSION}-${COMMIT_HASH}_${CMAKE_BUILD_TYPE}"
|
|
||||||
# Output the executable in main binary dir.
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
# Create symlink to the built executable.
|
|
||||||
add_custom_command(TARGET memgraph_distributed POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:memgraph_distributed> ${CMAKE_BINARY_DIR}/memgraph_distributed
|
|
||||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/memgraph_distributed
|
|
||||||
COMMENT Creating symlink to memgraph distributed executable)
|
|
||||||
|
|
||||||
# memgraph single node high availability executable
|
|
||||||
add_executable(memgraph_ha memgraph_ha.cpp)
|
|
||||||
target_link_libraries(memgraph_ha mg-single-node-ha kvstore_lib telemetry_lib)
|
|
||||||
set_target_properties(memgraph_ha PROPERTIES
|
|
||||||
# Set the executable output name to include version information.
|
|
||||||
OUTPUT_NAME "memgraph_ha-${memgraph_VERSION}-${COMMIT_HASH}_${CMAKE_BUILD_TYPE}"
|
|
||||||
# Output the executable in main binary dir.
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
# Create symlink to the built executable.
|
|
||||||
add_custom_command(TARGET memgraph_ha POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:memgraph_ha> ${CMAKE_BINARY_DIR}/memgraph_ha
|
|
||||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/memgraph_ha
|
|
||||||
COMMENT Creating symlink to memgraph single node high availability executable)
|
|
||||||
|
|||||||
@@ -10,12 +10,3 @@ add_library(mg-communication STATIC ${communication_src_files})
|
|||||||
target_link_libraries(mg-communication Threads::Threads mg-utils mg-io fmt glog gflags)
|
target_link_libraries(mg-communication Threads::Threads mg-utils mg-io fmt glog gflags)
|
||||||
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
|
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
|
||||||
target_include_directories(mg-communication SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
target_include_directories(mg-communication SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
set(communication_rpc_src_files
|
|
||||||
rpc/client.cpp
|
|
||||||
rpc/protocol.cpp
|
|
||||||
rpc/server.cpp)
|
|
||||||
|
|
||||||
add_library(mg-comm-rpc STATIC ${communication_rpc_src_files})
|
|
||||||
target_link_libraries(mg-comm-rpc Threads::Threads mg-communication mg-utils mg-io fmt glog gflags)
|
|
||||||
target_link_libraries(mg-comm-rpc mg-slk)
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#include "communication/bolt/v1/codes.hpp"
|
#include "communication/bolt/v1/codes.hpp"
|
||||||
#include "communication/bolt/v1/value.hpp"
|
#include "communication/bolt/v1/value.hpp"
|
||||||
#include "utils/bswap.hpp"
|
|
||||||
#include "utils/cast.hpp"
|
#include "utils/cast.hpp"
|
||||||
|
#include "utils/endian.hpp"
|
||||||
|
|
||||||
namespace communication::bolt {
|
namespace communication::bolt {
|
||||||
|
|
||||||
@@ -186,18 +186,18 @@ class Decoder {
|
|||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ret = utils::Bswap(tmp);
|
ret = utils::BigEndianToHost(tmp);
|
||||||
} else if (marker == Marker::Int32) {
|
} else if (marker == Marker::Int32) {
|
||||||
int32_t tmp;
|
int32_t tmp;
|
||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ret = utils::Bswap(tmp);
|
ret = utils::BigEndianToHost(tmp);
|
||||||
} else if (marker == Marker::Int64) {
|
} else if (marker == Marker::Int64) {
|
||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&ret), sizeof(ret))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&ret), sizeof(ret))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ret = utils::Bswap(ret);
|
ret = utils::BigEndianToHost(ret);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ class Decoder {
|
|||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&value), sizeof(value))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&value), sizeof(value))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
value = utils::Bswap(value);
|
value = utils::BigEndianToHost(value);
|
||||||
ret = utils::MemcpyCast<double>(value);
|
ret = utils::MemcpyCast<double>(value);
|
||||||
*data = Value(ret);
|
*data = Value(ret);
|
||||||
return true;
|
return true;
|
||||||
@@ -233,14 +233,14 @@ class Decoder {
|
|||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
tmp = utils::Bswap(tmp);
|
tmp = utils::BigEndianToHost(tmp);
|
||||||
return tmp;
|
return tmp;
|
||||||
} else if (marker == Marker32[type]) {
|
} else if (marker == Marker32[type]) {
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
tmp = utils::Bswap(tmp);
|
tmp = utils::BigEndianToHost(tmp);
|
||||||
return tmp;
|
return tmp;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
#include "communication/bolt/v1/codes.hpp"
|
#include "communication/bolt/v1/codes.hpp"
|
||||||
#include "communication/bolt/v1/value.hpp"
|
#include "communication/bolt/v1/value.hpp"
|
||||||
#include "utils/bswap.hpp"
|
|
||||||
#include "utils/cast.hpp"
|
#include "utils/cast.hpp"
|
||||||
|
#include "utils/endian.hpp"
|
||||||
|
|
||||||
static_assert(std::is_same_v<std::uint8_t, char> ||
|
static_assert(std::is_same_v<std::uint8_t, char> ||
|
||||||
std::is_same_v<std::uint8_t, unsigned char>,
|
std::is_same_v<std::uint8_t, unsigned char>,
|
||||||
@@ -216,7 +216,7 @@ class BaseEncoder {
|
|||||||
private:
|
private:
|
||||||
template <class T>
|
template <class T>
|
||||||
void WritePrimitiveValue(T value) {
|
void WritePrimitiveValue(T value) {
|
||||||
value = utils::Bswap(value);
|
value = utils::HostToBigEndian(value);
|
||||||
WriteRAW(reinterpret_cast<const uint8_t *>(&value), sizeof(value));
|
WriteRAW(reinterpret_cast<const uint8_t *>(&value), sizeof(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "communication/bolt/v1/constants.hpp"
|
#include "communication/bolt/v1/constants.hpp"
|
||||||
#include "utils/bswap.hpp"
|
|
||||||
|
|
||||||
namespace communication::bolt {
|
namespace communication::bolt {
|
||||||
|
|
||||||
|
|||||||
@@ -60,10 +60,6 @@ class Session {
|
|||||||
/** Aborts currently running query. */
|
/** Aborts currently running query. */
|
||||||
virtual void Abort() = 0;
|
virtual void Abort() = 0;
|
||||||
|
|
||||||
/** Return `true` if the user was successfully authenticated. */
|
|
||||||
virtual bool Authenticate(const std::string &username,
|
|
||||||
const std::string &password) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the session after data has been read into the buffer.
|
* Executes the session after data has been read into the buffer.
|
||||||
* Goes through the bolt states in order to execute commands from the client.
|
* Goes through the bolt states in order to execute commands from the client.
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ State HandleRun(TSession &session, State state, Marker marker) {
|
|||||||
std::vector<Value> vec;
|
std::vector<Value> vec;
|
||||||
std::map<std::string, Value> data;
|
std::map<std::string, Value> data;
|
||||||
vec.reserve(header.size());
|
vec.reserve(header.size());
|
||||||
for (auto &i : header) vec.push_back(Value(i));
|
for (auto &i : header) vec.emplace_back(std::move(i));
|
||||||
data.insert(std::make_pair(std::string("fields"), Value(vec)));
|
data.emplace("fields", std::move(vec));
|
||||||
// Send the header.
|
// Send the header.
|
||||||
if (!session.encoder_.MessageSuccess(data)) {
|
if (!session.encoder_.MessageSuccess(data)) {
|
||||||
DLOG(WARNING) << "Couldn't send query header!";
|
DLOG(WARNING) << "Couldn't send query header!";
|
||||||
|
|||||||
@@ -60,38 +60,6 @@ State StateInitRun(Session &session) {
|
|||||||
LOG(INFO) << fmt::format("Client connected '{}'", client_name.ValueString())
|
LOG(INFO) << fmt::format("Client connected '{}'", client_name.ValueString())
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
// Get authentication data.
|
|
||||||
std::string username, password;
|
|
||||||
auto &data = metadata.ValueMap();
|
|
||||||
if (!data.count("scheme")) {
|
|
||||||
LOG(WARNING) << "The client didn't supply authentication information!";
|
|
||||||
return State::Close;
|
|
||||||
}
|
|
||||||
if (data["scheme"].ValueString() == "basic") {
|
|
||||||
if (!data.count("principal") || !data.count("credentials")) {
|
|
||||||
LOG(WARNING) << "The client didn't supply authentication information!";
|
|
||||||
return State::Close;
|
|
||||||
}
|
|
||||||
username = data["principal"].ValueString();
|
|
||||||
password = data["credentials"].ValueString();
|
|
||||||
} else if (data["scheme"].ValueString() != "none") {
|
|
||||||
LOG(WARNING) << "Unsupported authentication scheme: "
|
|
||||||
<< data["scheme"].ValueString();
|
|
||||||
return State::Close;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate the user.
|
|
||||||
if (!session.Authenticate(username, password)) {
|
|
||||||
if (!session.encoder_.MessageFailure(
|
|
||||||
{{"code", "Memgraph.ClientError.Security.Unauthenticated"},
|
|
||||||
{"message", "Authentication failure"}})) {
|
|
||||||
DLOG(WARNING) << "Couldn't send failure message to the client!";
|
|
||||||
}
|
|
||||||
// Throw an exception to indicate to the network stack that the session
|
|
||||||
// should be closed and cleaned up.
|
|
||||||
throw SessionClosedException("The client is not authenticated!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return success.
|
// Return success.
|
||||||
if (!session.encoder_.MessageSuccess()) {
|
if (!session.encoder_.MessageSuccess()) {
|
||||||
DLOG(WARNING) << "Couldn't send success message to the client!";
|
DLOG(WARNING) << "Couldn't send success message to the client!";
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ class GraphDbAccessor {
|
|||||||
* @param check_empty If the vertex should be checked for existing edges
|
* @param check_empty If the vertex should be checked for existing edges
|
||||||
* before deletion.
|
* before deletion.
|
||||||
* @return If or not the vertex was deleted.
|
* @return If or not the vertex was deleted.
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
bool RemoveVertex(VertexAccessor &vertex_accessor, bool check_empty = true);
|
bool RemoveVertex(VertexAccessor &vertex_accessor, bool check_empty = true);
|
||||||
|
|
||||||
@@ -99,6 +101,9 @@ class GraphDbAccessor {
|
|||||||
* and incoming connections.
|
* and incoming connections.
|
||||||
*
|
*
|
||||||
* @param vertex_accessor Accessor to a vertex.
|
* @param vertex_accessor Accessor to a vertex.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
void DetachRemoveVertex(VertexAccessor &vertex_accessor);
|
void DetachRemoveVertex(VertexAccessor &vertex_accessor);
|
||||||
|
|
||||||
@@ -294,6 +299,9 @@ class GraphDbAccessor {
|
|||||||
* recovering from durability.
|
* recovering from durability.
|
||||||
*
|
*
|
||||||
* @return An accessor to the edge.
|
* @return An accessor to the edge.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
EdgeAccessor InsertEdge(VertexAccessor &from, VertexAccessor &to,
|
EdgeAccessor InsertEdge(VertexAccessor &from, VertexAccessor &to,
|
||||||
storage::EdgeType type,
|
storage::EdgeType type,
|
||||||
@@ -310,6 +318,9 @@ class GraphDbAccessor {
|
|||||||
* side.
|
* side.
|
||||||
* @param remove_in_edge If the edge should be removed from the its
|
* @param remove_in_edge If the edge should be removed from the its
|
||||||
* destination side.
|
* destination side.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
void RemoveEdge(EdgeAccessor &edge, bool remove_out_edge = true,
|
void RemoveEdge(EdgeAccessor &edge, bool remove_out_edge = true,
|
||||||
bool remove_in_edge = true);
|
bool remove_in_edge = true);
|
||||||
@@ -413,13 +424,14 @@ class GraphDbAccessor {
|
|||||||
*
|
*
|
||||||
* @param label - label to build for
|
* @param label - label to build for
|
||||||
* @param property - property to build for
|
* @param property - property to build for
|
||||||
|
* @throw IndexExistsException
|
||||||
|
* @throw TransactionException
|
||||||
*/
|
*/
|
||||||
void BuildIndex(storage::Label label, storage::Property property);
|
void BuildIndex(storage::Label label, storage::Property property);
|
||||||
|
|
||||||
/// Deletes the index responisble for (label, property).
|
/// Deletes the index responisble for (label, property).
|
||||||
///
|
///
|
||||||
/// @throws IndexTransactionException if it can't obtain a blocking
|
/// @throw TransactionException if it can't obtain a blocking transaction.
|
||||||
/// transaction.
|
|
||||||
void DeleteIndex(storage::Label label, storage::Property property);
|
void DeleteIndex(storage::Label label, storage::Property property);
|
||||||
|
|
||||||
/// Populates index with vertices containing the key
|
/// Populates index with vertices containing the key
|
||||||
@@ -433,10 +445,10 @@ class GraphDbAccessor {
|
|||||||
* properties.
|
* properties.
|
||||||
* If the constraint already exists, this method does nothing.
|
* If the constraint already exists, this method does nothing.
|
||||||
*
|
*
|
||||||
* @throws ConstraintViolationException if constraint couldn't be build
|
* @throw ConstraintViolationException if constraint couldn't be build
|
||||||
* due to existing constraint violation.
|
* due to existing constraint violation.
|
||||||
* @throws TransactionEngineError if the engine doesn't accept transactions.
|
* @throw TransactionEngineError if the engine doesn't accept transactions.
|
||||||
* @throws mvcc::SerializationError on serialization errors.
|
* @throw mvcc::SerializationError on serialization errors.
|
||||||
*/
|
*/
|
||||||
void BuildUniqueConstraint(storage::Label label,
|
void BuildUniqueConstraint(storage::Label label,
|
||||||
const std::vector<storage::Property> &properties);
|
const std::vector<storage::Property> &properties);
|
||||||
@@ -444,6 +456,7 @@ class GraphDbAccessor {
|
|||||||
/**
|
/**
|
||||||
* Deletes existing unique constraint.
|
* Deletes existing unique constraint.
|
||||||
* If the constraint doesn't exist, this method does nothing.
|
* If the constraint doesn't exist, this method does nothing.
|
||||||
|
* @throw TransactionException
|
||||||
*/
|
*/
|
||||||
void DeleteUniqueConstraint(storage::Label label,
|
void DeleteUniqueConstraint(storage::Label label,
|
||||||
const std::vector<storage::Property> &properties);
|
const std::vector<storage::Property> &properties);
|
||||||
@@ -574,7 +587,10 @@ class GraphDbAccessor {
|
|||||||
/** Returns the id of this accessor's transaction */
|
/** Returns the id of this accessor's transaction */
|
||||||
tx::TransactionId transaction_id() const;
|
tx::TransactionId transaction_id() const;
|
||||||
|
|
||||||
/** Advances transaction's command id by 1. */
|
/**
|
||||||
|
* Advances transaction's command id by 1.
|
||||||
|
* @throw TransactionException
|
||||||
|
*/
|
||||||
void AdvanceCommand();
|
void AdvanceCommand();
|
||||||
|
|
||||||
/** Commit transaction. */
|
/** Commit transaction. */
|
||||||
@@ -622,6 +638,9 @@ class GraphDbAccessor {
|
|||||||
* @param label - label that was added
|
* @param label - label that was added
|
||||||
* @param vertex_accessor - vertex_accessor that was updated
|
* @param vertex_accessor - vertex_accessor that was updated
|
||||||
* @param vertex - vertex that was updated
|
* @param vertex - vertex that was updated
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
* @throw ConstraintViolationException
|
||||||
*/
|
*/
|
||||||
void UpdateOnAddLabel(storage::Label label,
|
void UpdateOnAddLabel(storage::Label label,
|
||||||
const VertexAccessor &vertex_accessor,
|
const VertexAccessor &vertex_accessor,
|
||||||
@@ -632,6 +651,8 @@ class GraphDbAccessor {
|
|||||||
*
|
*
|
||||||
* @param label - label that was removed
|
* @param label - label that was removed
|
||||||
* @param vertex_accessor - vertex_accessor that was updated
|
* @param vertex_accessor - vertex_accessor that was updated
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
void UpdateOnRemoveLabel(storage::Label label,
|
void UpdateOnRemoveLabel(storage::Label label,
|
||||||
const RecordAccessor<Vertex> &accessor);
|
const RecordAccessor<Vertex> &accessor);
|
||||||
@@ -643,6 +664,8 @@ class GraphDbAccessor {
|
|||||||
* @param previous_value - previous value of the property
|
* @param previous_value - previous value of the property
|
||||||
* @param vertex_accessor - vertex_accessor that was updated
|
* @param vertex_accessor - vertex_accessor that was updated
|
||||||
* @param vertex - vertex that was updated
|
* @param vertex - vertex that was updated
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
void UpdateOnRemoveProperty(storage::Property property,
|
void UpdateOnRemoveProperty(storage::Property property,
|
||||||
const PropertyValue &previous_value,
|
const PropertyValue &previous_value,
|
||||||
@@ -657,6 +680,9 @@ class GraphDbAccessor {
|
|||||||
* @param new_value - new value of the property
|
* @param new_value - new value of the property
|
||||||
* @param vertex_accessor - vertex accessor that was updated
|
* @param vertex_accessor - vertex accessor that was updated
|
||||||
* @param vertex - vertex that was updated
|
* @param vertex - vertex that was updated
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
* @throw ConstraintViolationException
|
||||||
*/
|
*/
|
||||||
void UpdateOnAddProperty(storage::Property property,
|
void UpdateOnAddProperty(storage::Property property,
|
||||||
const PropertyValue &previous_value,
|
const PropertyValue &previous_value,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "hasher.hpp"
|
#include "hasher.hpp"
|
||||||
#include "utils/bswap.hpp"
|
#include "utils/endian.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer reads data from file and calculates hash of read data. Implements
|
* Buffer reads data from file and calculates hash of read data. Implements
|
||||||
@@ -50,8 +50,7 @@ class HashedFileReader {
|
|||||||
bool ReadType(TValue &val, bool hash = true) {
|
bool ReadType(TValue &val, bool hash = true) {
|
||||||
if (!Read(reinterpret_cast<uint8_t *>(&val), sizeof(TValue), hash))
|
if (!Read(reinterpret_cast<uint8_t *>(&val), sizeof(TValue), hash))
|
||||||
return false;
|
return false;
|
||||||
// TODO: must be platform specific in the future
|
val = utils::BigEndianToHost(val);
|
||||||
val = utils::Bswap(val);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "hasher.hpp"
|
#include "hasher.hpp"
|
||||||
#include "utils/bswap.hpp"
|
#include "utils/endian.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer that writes data to file and calculates hash of written data.
|
* Buffer that writes data to file and calculates hash of written data.
|
||||||
@@ -54,9 +54,8 @@ class HashedFileWriter {
|
|||||||
*/
|
*/
|
||||||
template <typename TValue>
|
template <typename TValue>
|
||||||
void WriteValue(const TValue &val, bool hash = true) {
|
void WriteValue(const TValue &val, bool hash = true) {
|
||||||
TValue val_bswapped = utils::Bswap(val);
|
TValue val_big = utils::HostToBigEndian(val);
|
||||||
Write(reinterpret_cast<const uint8_t *>(&val_bswapped), sizeof(TValue),
|
Write(reinterpret_cast<const uint8_t *>(&val_big), sizeof(TValue), hash);
|
||||||
hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO try to remove before diff
|
// TODO try to remove before diff
|
||||||
|
|||||||
@@ -66,10 +66,6 @@ bool VersionConsistency(const fs::path &durability_dir) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DistributedVersionConsistency(const int64_t master_version) {
|
|
||||||
return durability::kVersion == master_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ContainsDurabilityFiles(const fs::path &durability_dir) {
|
bool ContainsDurabilityFiles(const fs::path &durability_dir) {
|
||||||
for (const auto &durability_type : {kSnapshotDir, kWalDir}) {
|
for (const auto &durability_type : {kSnapshotDir, kWalDir}) {
|
||||||
auto recovery_dir = durability_dir / durability_type;
|
auto recovery_dir = durability_dir / durability_type;
|
||||||
|
|||||||
@@ -84,15 +84,6 @@ bool ReadSnapshotSummary(HashedFileReader &buffer, int64_t &vertex_count,
|
|||||||
*/
|
*/
|
||||||
bool VersionConsistency(const std::filesystem::path &durability_dir);
|
bool VersionConsistency(const std::filesystem::path &durability_dir);
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the current memgraph binary (on a worker) is
|
|
||||||
* version consistent with the cluster master.
|
|
||||||
*
|
|
||||||
* @param master_version - Version of the master.
|
|
||||||
* @return - True if versions match.
|
|
||||||
*/
|
|
||||||
bool DistributedVersionConsistency(const int64_t master_version);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the durability directory contains snapshot
|
* Checks whether the durability directory contains snapshot
|
||||||
* or write-ahead log file.
|
* or write-ahead log file.
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ query::TypedValue ToTypedValue(const Value &value) {
|
|||||||
std::vector<query::TypedValue> list;
|
std::vector<query::TypedValue> list;
|
||||||
list.reserve(value.ValueList().size());
|
list.reserve(value.ValueList().size());
|
||||||
for (const auto &v : value.ValueList()) list.push_back(ToTypedValue(v));
|
for (const auto &v : value.ValueList()) list.push_back(ToTypedValue(v));
|
||||||
return query::TypedValue(list);
|
return query::TypedValue(std::move(list));
|
||||||
}
|
}
|
||||||
case Value::Type::Map: {
|
case Value::Type::Map: {
|
||||||
std::map<std::string, query::TypedValue> map;
|
std::map<std::string, query::TypedValue> map;
|
||||||
for (const auto &kv : value.ValueMap())
|
for (const auto &kv : value.ValueMap())
|
||||||
map.emplace(kv.first, ToTypedValue(kv.second));
|
map.emplace(kv.first, ToTypedValue(kv.second));
|
||||||
return query::TypedValue(map);
|
return query::TypedValue(std::move(map));
|
||||||
}
|
}
|
||||||
case Value::Type::Vertex:
|
case Value::Type::Vertex:
|
||||||
case Value::Type::Edge:
|
case Value::Type::Edge:
|
||||||
@@ -61,14 +61,14 @@ Value ToBoltValue(const query::TypedValue &value) {
|
|||||||
for (const auto &v : value.ValueList()) {
|
for (const auto &v : value.ValueList()) {
|
||||||
values.push_back(ToBoltValue(v));
|
values.push_back(ToBoltValue(v));
|
||||||
}
|
}
|
||||||
return Value(values);
|
return Value(std::move(values));
|
||||||
}
|
}
|
||||||
case query::TypedValue::Type::Map: {
|
case query::TypedValue::Type::Map: {
|
||||||
std::map<std::string, Value> map;
|
std::map<std::string, Value> map;
|
||||||
for (const auto &kv : value.ValueMap()) {
|
for (const auto &kv : value.ValueMap()) {
|
||||||
map.emplace(kv.first, ToBoltValue(kv.second));
|
map.emplace(kv.first, ToBoltValue(kv.second));
|
||||||
}
|
}
|
||||||
return Value(map);
|
return Value(std::move(map));
|
||||||
}
|
}
|
||||||
case query::TypedValue::Type::Vertex:
|
case query::TypedValue::Type::Vertex:
|
||||||
return Value(ToBoltVertex(value.ValueVertex()));
|
return Value(ToBoltVertex(value.ValueVertex()));
|
||||||
@@ -91,7 +91,8 @@ communication::bolt::Vertex ToBoltVertex(const VertexAccessor &vertex) {
|
|||||||
properties[vertex.db_accessor().PropertyName(prop.first)] =
|
properties[vertex.db_accessor().PropertyName(prop.first)] =
|
||||||
ToBoltValue(prop.second);
|
ToBoltValue(prop.second);
|
||||||
}
|
}
|
||||||
return communication::bolt::Vertex{id, labels, properties};
|
return communication::bolt::Vertex{id, std::move(labels),
|
||||||
|
std::move(properties)};
|
||||||
}
|
}
|
||||||
|
|
||||||
communication::bolt::Edge ToBoltEdge(const EdgeAccessor &edge) {
|
communication::bolt::Edge ToBoltEdge(const EdgeAccessor &edge) {
|
||||||
@@ -104,7 +105,7 @@ communication::bolt::Edge ToBoltEdge(const EdgeAccessor &edge) {
|
|||||||
properties[edge.db_accessor().PropertyName(prop.first)] =
|
properties[edge.db_accessor().PropertyName(prop.first)] =
|
||||||
ToBoltValue(prop.second);
|
ToBoltValue(prop.second);
|
||||||
}
|
}
|
||||||
return communication::bolt::Edge{id, from, to, type, properties};
|
return communication::bolt::Edge{id, from, to, type, std::move(properties)};
|
||||||
}
|
}
|
||||||
|
|
||||||
communication::bolt::Path ToBoltPath(const query::Path &path) {
|
communication::bolt::Path ToBoltPath(const query::Path &path) {
|
||||||
@@ -175,7 +176,7 @@ Value ToBoltValue(const PropertyValue &value) {
|
|||||||
for (const auto &v : values) {
|
for (const auto &v : values) {
|
||||||
vec.push_back(ToBoltValue(v));
|
vec.push_back(ToBoltValue(v));
|
||||||
}
|
}
|
||||||
return Value(vec);
|
return Value(std::move(vec));
|
||||||
}
|
}
|
||||||
case PropertyValue::Type::Map: {
|
case PropertyValue::Type::Map: {
|
||||||
const auto &map = value.Value<std::map<std::string, PropertyValue>>();
|
const auto &map = value.Value<std::map<std::string, PropertyValue>>();
|
||||||
@@ -183,7 +184,7 @@ Value ToBoltValue(const PropertyValue &value) {
|
|||||||
for (const auto &kv : map) {
|
for (const auto &kv : map) {
|
||||||
dv_map.emplace(kv.first, ToBoltValue(kv.second));
|
dv_map.emplace(kv.first, ToBoltValue(kv.second));
|
||||||
}
|
}
|
||||||
return Value(dv_map);
|
return Value(std::move(dv_map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,18 @@ macro(define_add_lcp name main_src_files generated_lcp_files)
|
|||||||
# NOTE: ${${}ARGN} syntax escapes evaluating macro's ARGN variable; see:
|
# NOTE: ${${}ARGN} syntax escapes evaluating macro's ARGN variable; see:
|
||||||
# https://stackoverflow.com/questions/50365544/how-to-access-enclosing-functions-arguments-from-within-a-macro
|
# https://stackoverflow.com/questions/50365544/how-to-access-enclosing-functions-arguments-from-within-a-macro
|
||||||
cmake_parse_arguments(KW "${options}" "${one_value_kwargs}" "${multi_value_kwargs}" ${${}ARGN})
|
cmake_parse_arguments(KW "${options}" "${one_value_kwargs}" "${multi_value_kwargs}" ${${}ARGN})
|
||||||
string(REGEX REPLACE "\.lcp$" ".hpp" h_file
|
if (IS_ABSOLUTE ${lcp_file})
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}")
|
string(REGEX REPLACE "\.lcp$" ".hpp" h_file ${lcp_file})
|
||||||
|
else()
|
||||||
|
string(REGEX REPLACE "\.lcp$" ".hpp" h_file
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}")
|
||||||
|
endif()
|
||||||
if (NOT KW_NO_CPP)
|
if (NOT KW_NO_CPP)
|
||||||
set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}.cpp)
|
if (IS_ABSOLUTE ${lcp_file})
|
||||||
|
set(cpp_file ${lcp_file}.cpp)
|
||||||
|
else()
|
||||||
|
set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}.cpp)
|
||||||
|
endif()
|
||||||
# Update *global* main_src_files
|
# Update *global* main_src_files
|
||||||
set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE)
|
set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
@@ -68,7 +76,7 @@ macro(define_add_lcp name main_src_files generated_lcp_files)
|
|||||||
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
|
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
|
||||||
${CMAKE_SOURCE_DIR}/src/lisp/debug.lisp
|
${CMAKE_SOURCE_DIR}/src/lisp/debug.lisp
|
||||||
${CMAKE_SOURCE_DIR}/src/lisp/test.lisp
|
${CMAKE_SOURCE_DIR}/src/lisp/test.lisp
|
||||||
${CMAKE_SOURCE_DIR}/src/lisp/util.lisp
|
${CMAKE_SOURCE_DIR}/src/lisp/util.lisp
|
||||||
${CMAKE_SOURCE_DIR}/tools/lcp)
|
${CMAKE_SOURCE_DIR}/tools/lcp)
|
||||||
add_custom_command(OUTPUT ${h_file} ${cpp_file}
|
add_custom_command(OUTPUT ${h_file} ${cpp_file}
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/tools/lcp ${lcp_file} ${slk_serialize}
|
COMMAND ${CMAKE_SOURCE_DIR}/tools/lcp ${lcp_file} ${slk_serialize}
|
||||||
|
|||||||
@@ -9,11 +9,10 @@
|
|||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "memgraph_init.hpp"
|
||||||
|
|
||||||
#include "communication/server.hpp"
|
#include "communication/server.hpp"
|
||||||
#include "database/single_node/graph_db.hpp"
|
#include "database/single_node/graph_db.hpp"
|
||||||
#include "integrations/kafka/exceptions.hpp"
|
|
||||||
#include "integrations/kafka/streams.hpp"
|
|
||||||
#include "memgraph_init.hpp"
|
|
||||||
#include "query/exceptions.hpp"
|
#include "query/exceptions.hpp"
|
||||||
#include "telemetry/telemetry.hpp"
|
#include "telemetry/telemetry.hpp"
|
||||||
#include "utils/flag_validation.hpp"
|
#include "utils/flag_validation.hpp"
|
||||||
@@ -39,77 +38,19 @@ DEFINE_bool(telemetry_enabled, false,
|
|||||||
"the database runtime (vertex and edge counts and resource usage) "
|
"the database runtime (vertex and edge counts and resource usage) "
|
||||||
"to allow for easier improvement of the product.");
|
"to allow for easier improvement of the product.");
|
||||||
|
|
||||||
// Audit logging flags.
|
|
||||||
DEFINE_bool(audit_enabled, false, "Set to true to enable audit logging.");
|
|
||||||
DEFINE_VALIDATED_int32(audit_buffer_size, audit::kBufferSizeDefault,
|
|
||||||
"Maximum number of items in the audit log buffer.",
|
|
||||||
FLAG_IN_RANGE(1, INT32_MAX));
|
|
||||||
DEFINE_VALIDATED_int32(
|
|
||||||
audit_buffer_flush_interval_ms, audit::kBufferFlushIntervalMillisDefault,
|
|
||||||
"Interval (in milliseconds) used for flushing the audit log buffer.",
|
|
||||||
FLAG_IN_RANGE(10, INT32_MAX));
|
|
||||||
|
|
||||||
using ServerT = communication::Server<BoltSession, SessionData>;
|
using ServerT = communication::Server<BoltSession, SessionData>;
|
||||||
using communication::ServerContext;
|
using communication::ServerContext;
|
||||||
|
|
||||||
void SingleNodeMain() {
|
void SingleNodeMain() {
|
||||||
google::SetUsageMessage("Memgraph single-node database server");
|
google::SetUsageMessage("Memgraph single-node database server");
|
||||||
|
|
||||||
// All enterprise features should be constructed before the main database
|
|
||||||
// storage. This will cause them to be destructed *after* the main database
|
|
||||||
// storage. That way any errors that happen during enterprise features
|
|
||||||
// destruction won't have an impact on the storage engine.
|
|
||||||
// Example: When the main storage is destructed it makes a snapshot. When
|
|
||||||
// audit logging is destructed it syncs all pending data to disk and that can
|
|
||||||
// fail. That is why it must be destructed *after* the main database storage
|
|
||||||
// to minimise the impact of their failure on the main storage.
|
|
||||||
|
|
||||||
// Begin enterprise features initialization
|
|
||||||
|
|
||||||
auto durability_directory = std::filesystem::path(FLAGS_durability_directory);
|
auto durability_directory = std::filesystem::path(FLAGS_durability_directory);
|
||||||
|
|
||||||
// Auth
|
|
||||||
auth::Init();
|
|
||||||
auth::Auth auth{durability_directory / "auth"};
|
|
||||||
|
|
||||||
// Audit log
|
|
||||||
audit::Log audit_log{durability_directory / "audit", FLAGS_audit_buffer_size,
|
|
||||||
FLAGS_audit_buffer_flush_interval_ms};
|
|
||||||
// Start the log if enabled.
|
|
||||||
if (FLAGS_audit_enabled) {
|
|
||||||
audit_log.Start();
|
|
||||||
}
|
|
||||||
// Setup SIGUSR2 to be used for reopening audit log files, when e.g. logrotate
|
|
||||||
// rotates our audit logs.
|
|
||||||
CHECK(utils::SignalHandler::RegisterHandler(
|
|
||||||
utils::Signal::User2, [&audit_log]() { audit_log.ReopenLog(); }))
|
|
||||||
<< "Unable to register SIGUSR2 handler!";
|
|
||||||
|
|
||||||
// End enterprise features initialization
|
|
||||||
|
|
||||||
// Main storage and execution engines initialization
|
// Main storage and execution engines initialization
|
||||||
|
|
||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
query::Interpreter interpreter;
|
query::Interpreter interpreter;
|
||||||
SessionData session_data{&db, &interpreter, &auth, &audit_log};
|
SessionData session_data{&db, &interpreter};
|
||||||
|
|
||||||
integrations::kafka::Streams kafka_streams{
|
|
||||||
durability_directory / "streams",
|
|
||||||
[&session_data](
|
|
||||||
const std::string &query,
|
|
||||||
const std::map<std::string, communication::bolt::Value> ¶ms) {
|
|
||||||
KafkaStreamWriter(session_data, query, params);
|
|
||||||
}};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Recover possible streams.
|
|
||||||
kafka_streams.Recover();
|
|
||||||
} catch (const integrations::kafka::KafkaStreamException &e) {
|
|
||||||
LOG(ERROR) << e.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
session_data.interpreter->auth_ = &auth;
|
|
||||||
session_data.interpreter->kafka_streams_ = &kafka_streams;
|
|
||||||
|
|
||||||
ServerContext context;
|
ServerContext context;
|
||||||
std::string service_name = "Bolt";
|
std::string service_name = "Bolt";
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "glue/auth.hpp"
|
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
#include "query/exceptions.hpp"
|
#include "query/exceptions.hpp"
|
||||||
#include "requests/requests.hpp"
|
#include "requests/requests.hpp"
|
||||||
@@ -29,8 +28,6 @@ BoltSession::BoltSession(SessionData *data,
|
|||||||
communication::OutputStream>(input_stream,
|
communication::OutputStream>(input_stream,
|
||||||
output_stream),
|
output_stream),
|
||||||
transaction_engine_(data->db, data->interpreter),
|
transaction_engine_(data->db, data->interpreter),
|
||||||
auth_(data->auth),
|
|
||||||
audit_log_(data->audit_log),
|
|
||||||
endpoint_(endpoint) {}
|
endpoint_(endpoint) {}
|
||||||
|
|
||||||
using TEncoder =
|
using TEncoder =
|
||||||
@@ -43,28 +40,8 @@ std::vector<std::string> BoltSession::Interpret(
|
|||||||
std::map<std::string, PropertyValue> params_pv;
|
std::map<std::string, PropertyValue> params_pv;
|
||||||
for (const auto &kv : params)
|
for (const auto &kv : params)
|
||||||
params_pv.emplace(kv.first, glue::ToPropertyValue(kv.second));
|
params_pv.emplace(kv.first, glue::ToPropertyValue(kv.second));
|
||||||
#ifndef MG_SINGLE_NODE_HA
|
|
||||||
audit_log_->Record(endpoint_.address(), user_ ? user_->username() : "", query,
|
|
||||||
params_pv);
|
|
||||||
#endif
|
|
||||||
try {
|
try {
|
||||||
auto result = transaction_engine_.Interpret(query, params_pv);
|
return transaction_engine_.Interpret(query, params_pv);
|
||||||
#ifndef MG_SINGLE_NODE_HA
|
|
||||||
if (user_) {
|
|
||||||
const auto &permissions = user_->GetPermissions();
|
|
||||||
for (const auto &privilege : result.second) {
|
|
||||||
if (permissions.Has(glue::PrivilegeToPermission(privilege)) !=
|
|
||||||
auth::PermissionLevel::GRANT) {
|
|
||||||
transaction_engine_.Abort();
|
|
||||||
throw communication::bolt::ClientError(
|
|
||||||
"You are not authorized to execute this query! Please contact "
|
|
||||||
"your database administrator.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return result.first;
|
|
||||||
|
|
||||||
} catch (const query::QueryException &e) {
|
} catch (const query::QueryException &e) {
|
||||||
// Wrap QueryException into ClientError, because we want to allow the
|
// Wrap QueryException into ClientError, because we want to allow the
|
||||||
// client to fix their query.
|
// client to fix their query.
|
||||||
@@ -91,17 +68,6 @@ std::map<std::string, communication::bolt::Value> BoltSession::PullAll(
|
|||||||
|
|
||||||
void BoltSession::Abort() { transaction_engine_.Abort(); }
|
void BoltSession::Abort() { transaction_engine_.Abort(); }
|
||||||
|
|
||||||
bool BoltSession::Authenticate(const std::string &username,
|
|
||||||
const std::string &password) {
|
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
if (!auth_->HasUsers()) return true;
|
|
||||||
user_ = auth_->Authenticate(username, password);
|
|
||||||
return !!user_;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BoltSession::TypedValueResultStream::TypedValueResultStream(TEncoder *encoder)
|
BoltSession::TypedValueResultStream::TypedValueResultStream(TEncoder *encoder)
|
||||||
: encoder_(encoder) {}
|
: encoder_(encoder) {}
|
||||||
|
|
||||||
@@ -115,33 +81,6 @@ void BoltSession::TypedValueResultStream::Result(
|
|||||||
encoder_->MessageRecord(decoded_values);
|
encoder_->MessageRecord(decoded_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KafkaStreamWriter(
|
|
||||||
SessionData &session_data, const std::string &query,
|
|
||||||
const std::map<std::string, communication::bolt::Value> ¶ms) {
|
|
||||||
auto dba = session_data.db->Access();
|
|
||||||
KafkaResultStream stream;
|
|
||||||
std::map<std::string, PropertyValue> params_pv;
|
|
||||||
for (const auto &kv : params)
|
|
||||||
params_pv.emplace(kv.first, glue::ToPropertyValue(kv.second));
|
|
||||||
try {
|
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
(*session_data.interpreter)(query, dba, params_pv, false).PullAll(stream);
|
|
||||||
dba.Commit();
|
|
||||||
#else
|
|
||||||
(*session_data.interpreter)(query, *dba, params_pv, false).PullAll(stream);
|
|
||||||
dba->Commit();
|
|
||||||
#endif
|
|
||||||
} catch (const utils::BasicException &e) {
|
|
||||||
LOG(WARNING) << "[Kafka] query execution failed with an exception: "
|
|
||||||
<< e.what();
|
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
dba.Abort();
|
|
||||||
#else
|
|
||||||
dba->Abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Needed to correctly handle memgraph destruction from a signal handler.
|
// Needed to correctly handle memgraph destruction from a signal handler.
|
||||||
// Without having some sort of a flag, it is possible that a signal is handled
|
// Without having some sort of a flag, it is possible that a signal is handled
|
||||||
// when we are exiting main, inside destructors of database::GraphDb and
|
// when we are exiting main, inside destructors of database::GraphDb and
|
||||||
|
|||||||
@@ -9,14 +9,13 @@
|
|||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include "audit/log.hpp"
|
#include "query/interpreter.hpp"
|
||||||
#include "auth/auth.hpp"
|
#include "query/transaction_engine.hpp"
|
||||||
|
|
||||||
#include "communication/bolt/v1/exceptions.hpp"
|
#include "communication/bolt/v1/exceptions.hpp"
|
||||||
#include "communication/bolt/v1/session.hpp"
|
#include "communication/bolt/v1/session.hpp"
|
||||||
#include "communication/init.hpp"
|
#include "communication/init.hpp"
|
||||||
#include "communication/session.hpp"
|
#include "communication/session.hpp"
|
||||||
#include "query/interpreter.hpp"
|
|
||||||
#include "query/transaction_engine.hpp"
|
|
||||||
|
|
||||||
DECLARE_string(durability_directory);
|
DECLARE_string(durability_directory);
|
||||||
|
|
||||||
@@ -25,16 +24,11 @@ DECLARE_string(durability_directory);
|
|||||||
struct SessionData {
|
struct SessionData {
|
||||||
// Explicit constructor here to ensure that pointers to all objects are
|
// Explicit constructor here to ensure that pointers to all objects are
|
||||||
// supplied.
|
// supplied.
|
||||||
SessionData(database::GraphDb *_db, query::Interpreter *_interpreter,
|
SessionData(database::GraphDb *_db, query::Interpreter *_interpreter)
|
||||||
auth::Auth *_auth, audit::Log *_audit_log)
|
|
||||||
: db(_db),
|
: db(_db),
|
||||||
interpreter(_interpreter),
|
interpreter(_interpreter) {}
|
||||||
auth(_auth),
|
|
||||||
audit_log(_audit_log) {}
|
|
||||||
database::GraphDb *db;
|
database::GraphDb *db;
|
||||||
query::Interpreter *interpreter;
|
query::Interpreter *interpreter;
|
||||||
auth::Auth *auth;
|
|
||||||
audit::Log *audit_log;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BoltSession final
|
class BoltSession final
|
||||||
@@ -57,9 +51,6 @@ class BoltSession final
|
|||||||
|
|
||||||
void Abort() override;
|
void Abort() override;
|
||||||
|
|
||||||
bool Authenticate(const std::string &username,
|
|
||||||
const std::string &password) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Wrapper around TEncoder which converts TypedValue to Value
|
/// Wrapper around TEncoder which converts TypedValue to Value
|
||||||
/// before forwarding the calls to original TEncoder.
|
/// before forwarding the calls to original TEncoder.
|
||||||
@@ -74,26 +65,9 @@ class BoltSession final
|
|||||||
};
|
};
|
||||||
|
|
||||||
query::TransactionEngine transaction_engine_;
|
query::TransactionEngine transaction_engine_;
|
||||||
auth::Auth *auth_;
|
|
||||||
std::optional<auth::User> user_;
|
|
||||||
audit::Log *audit_log_;
|
|
||||||
io::network::Endpoint endpoint_;
|
io::network::Endpoint endpoint_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Class that implements ResultStream API for Kafka.
|
|
||||||
///
|
|
||||||
/// Kafka doesn't need to stream the import results back to the client so we
|
|
||||||
/// don't need any functionality here.
|
|
||||||
class KafkaResultStream {
|
|
||||||
public:
|
|
||||||
void Result(const std::vector<query::TypedValue> &) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Writes data streamed from kafka to memgraph.
|
|
||||||
void KafkaStreamWriter(
|
|
||||||
SessionData &session_data, const std::string &query,
|
|
||||||
const std::map<std::string, communication::bolt::Value> ¶ms);
|
|
||||||
|
|
||||||
/// Set up signal handlers and register `shutdown` on SIGTERM and SIGINT.
|
/// Set up signal handlers and register `shutdown` on SIGTERM and SIGINT.
|
||||||
/// In most cases you don't have to call this. If you are using a custom server
|
/// In most cases you don't have to call this. If you are using a custom server
|
||||||
/// startup function for `WithInit`, then you probably need to use this to
|
/// startup function for `WithInit`, then you probably need to use this to
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
|
|
||||||
|
|
||||||
struct EvaluationContext {
|
struct EvaluationContext {
|
||||||
/// Memory for allocations during evaluation of a *single* Pull call.
|
/// Memory for allocations during evaluation of a *single* Pull call.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -122,19 +122,4 @@ class RemoveAttachedVertexException : public QueryRuntimeException {
|
|||||||
"connections. Consider using DETACH DELETE.") {}
|
"connections. Consider using DETACH DELETE.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserModificationInMulticommandTxException : public QueryException {
|
|
||||||
public:
|
|
||||||
UserModificationInMulticommandTxException()
|
|
||||||
: QueryException(
|
|
||||||
"Authentication clause not allowed in multicommand transactions.") {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class StreamClauseInMulticommandTxException : public QueryException {
|
|
||||||
public:
|
|
||||||
StreamClauseInMulticommandTxException()
|
|
||||||
: QueryException(
|
|
||||||
"Stream clause not allowed in multicommand transactions.") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
|||||||
@@ -2047,121 +2047,11 @@ cpp<#
|
|||||||
(:serialize (:slk))
|
(:serialize (:slk))
|
||||||
(:clone))
|
(:clone))
|
||||||
|
|
||||||
(lcp:define-class auth-query (query)
|
|
||||||
((action "Action" :scope :public)
|
|
||||||
(user "std::string" :scope :public)
|
|
||||||
(role "std::string" :scope :public)
|
|
||||||
(user-or-role "std::string" :scope :public)
|
|
||||||
(password "Expression *" :initval "nullptr" :scope :public
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(privileges "std::vector<Privilege>" :scope :public))
|
|
||||||
(:public
|
|
||||||
(lcp:define-enum action
|
|
||||||
(create-role drop-role show-roles create-user set-password drop-user
|
|
||||||
show-users set-role clear-role grant-privilege deny-privilege
|
|
||||||
revoke-privilege show-privileges show-role-for-user
|
|
||||||
show-users-for-role)
|
|
||||||
(:serialize))
|
|
||||||
(lcp:define-enum privilege
|
|
||||||
(create delete match merge set remove index stats auth stream constraint
|
|
||||||
dump)
|
|
||||||
(:serialize))
|
|
||||||
#>cpp
|
|
||||||
AuthQuery() = default;
|
|
||||||
|
|
||||||
DEFVISITABLE(QueryVisitor<void>);
|
|
||||||
cpp<#)
|
|
||||||
(:protected
|
|
||||||
#>cpp
|
|
||||||
AuthQuery(Action action, std::string user, std::string role,
|
|
||||||
std::string user_or_role, Expression *password,
|
|
||||||
std::vector<Privilege> privileges)
|
|
||||||
: action_(action),
|
|
||||||
user_(user),
|
|
||||||
role_(role),
|
|
||||||
user_or_role_(user_or_role),
|
|
||||||
password_(password),
|
|
||||||
privileges_(privileges) {}
|
|
||||||
cpp<#)
|
|
||||||
(:private
|
|
||||||
#>cpp
|
|
||||||
friend class AstStorage;
|
|
||||||
cpp<#)
|
|
||||||
(:serialize (:slk))
|
|
||||||
(:clone))
|
|
||||||
|
|
||||||
;; TODO: Generate this via LCP
|
|
||||||
#>cpp
|
|
||||||
/// Constant that holds all available privileges.
|
|
||||||
const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
|
|
||||||
AuthQuery::Privilege::CREATE, AuthQuery::Privilege::DELETE,
|
|
||||||
AuthQuery::Privilege::MATCH, AuthQuery::Privilege::MERGE,
|
|
||||||
AuthQuery::Privilege::SET, AuthQuery::Privilege::REMOVE,
|
|
||||||
AuthQuery::Privilege::INDEX, AuthQuery::Privilege::STATS,
|
|
||||||
AuthQuery::Privilege::AUTH, AuthQuery::Privilege::STREAM,
|
|
||||||
AuthQuery::Privilege::CONSTRAINT, AuthQuery::Privilege::DUMP};
|
|
||||||
cpp<#
|
|
||||||
|
|
||||||
(lcp:define-class stream-query (query)
|
|
||||||
((action "Action" :scope :public)
|
|
||||||
(stream-name "std::string" :scope :public)
|
|
||||||
(stream-uri "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(stream-topic "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(transform-uri "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(batch-interval-in-ms "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(batch-size "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression"))
|
|
||||||
(limit-batches "Expression *" :scope :public :initval "nullptr"
|
|
||||||
:slk-save #'slk-save-ast-pointer
|
|
||||||
:slk-load (slk-load-ast-pointer "Expression")))
|
|
||||||
(:public
|
|
||||||
(lcp:define-enum action
|
|
||||||
(create-stream drop-stream show-streams start-stream stop-stream
|
|
||||||
start-all-streams stop-all-streams test-stream)
|
|
||||||
(:serialize))
|
|
||||||
|
|
||||||
#>cpp
|
|
||||||
StreamQuery() = default;
|
|
||||||
|
|
||||||
DEFVISITABLE(QueryVisitor<void>);
|
|
||||||
cpp<#)
|
|
||||||
(:protected
|
|
||||||
#>cpp
|
|
||||||
StreamQuery(Action action, std::string stream_name, Expression *stream_uri,
|
|
||||||
Expression *stream_topic, Expression *transform_uri,
|
|
||||||
Expression *batch_interval_in_ms, Expression *batch_size,
|
|
||||||
Expression *limit_batches)
|
|
||||||
: action_(action),
|
|
||||||
stream_name_(std::move(stream_name)),
|
|
||||||
stream_uri_(stream_uri),
|
|
||||||
stream_topic_(stream_topic),
|
|
||||||
transform_uri_(transform_uri),
|
|
||||||
batch_interval_in_ms_(batch_interval_in_ms),
|
|
||||||
batch_size_(batch_size),
|
|
||||||
limit_batches_(limit_batches) {}
|
|
||||||
cpp<#)
|
|
||||||
(:private
|
|
||||||
#>cpp
|
|
||||||
friend class AstStorage;
|
|
||||||
cpp<#)
|
|
||||||
(:serialize (:slk))
|
|
||||||
(:clone))
|
|
||||||
|
|
||||||
(lcp:define-class info-query (query)
|
(lcp:define-class info-query (query)
|
||||||
((info-type "InfoType" :scope :public))
|
((info-type "InfoType" :scope :public))
|
||||||
(:public
|
(:public
|
||||||
(lcp:define-enum info-type
|
(lcp:define-enum info-type
|
||||||
(storage index constraint raft)
|
(storage index constraint)
|
||||||
(:serialize))
|
(:serialize))
|
||||||
|
|
||||||
#>cpp
|
#>cpp
|
||||||
|
|||||||
@@ -61,11 +61,9 @@ class RemoveProperty;
|
|||||||
class RemoveLabels;
|
class RemoveLabels;
|
||||||
class Merge;
|
class Merge;
|
||||||
class Unwind;
|
class Unwind;
|
||||||
class AuthQuery;
|
|
||||||
class ExplainQuery;
|
class ExplainQuery;
|
||||||
class ProfileQuery;
|
class ProfileQuery;
|
||||||
class IndexQuery;
|
class IndexQuery;
|
||||||
class StreamQuery;
|
|
||||||
class InfoQuery;
|
class InfoQuery;
|
||||||
class ConstraintQuery;
|
class ConstraintQuery;
|
||||||
class RegexMatch;
|
class RegexMatch;
|
||||||
@@ -113,7 +111,7 @@ class ExpressionVisitor
|
|||||||
template <class TResult>
|
template <class TResult>
|
||||||
class QueryVisitor
|
class QueryVisitor
|
||||||
: public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery,
|
: public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery,
|
||||||
IndexQuery, AuthQuery, StreamQuery, InfoQuery,
|
IndexQuery, InfoQuery,
|
||||||
ConstraintQuery, DumpQuery> {};
|
ConstraintQuery, DumpQuery> {};
|
||||||
|
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
|||||||
@@ -61,9 +61,6 @@ antlrcpp::Any CypherMainVisitor::visitInfoQuery(
|
|||||||
} else if (ctx->constraintInfo()) {
|
} else if (ctx->constraintInfo()) {
|
||||||
info_query->info_type_ = InfoQuery::InfoType::CONSTRAINT;
|
info_query->info_type_ = InfoQuery::InfoType::CONSTRAINT;
|
||||||
return info_query;
|
return info_query;
|
||||||
} else if (ctx->raftInfo()) {
|
|
||||||
info_query->info_type_ = InfoQuery::InfoType::RAFT;
|
|
||||||
return info_query;
|
|
||||||
} else {
|
} else {
|
||||||
throw utils::NotYetImplemented("Info query: '{}'", ctx->getText());
|
throw utils::NotYetImplemented("Info query: '{}'", ctx->getText());
|
||||||
}
|
}
|
||||||
@@ -169,31 +166,6 @@ antlrcpp::Any CypherMainVisitor::visitDropIndex(
|
|||||||
return index_query;
|
return index_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitAuthQuery(
|
|
||||||
MemgraphCypher::AuthQueryContext *ctx) {
|
|
||||||
CHECK(ctx->children.size() == 1)
|
|
||||||
<< "AuthQuery should have exactly one child!";
|
|
||||||
auto *auth_query = ctx->children[0]->accept(this).as<AuthQuery *>();
|
|
||||||
query_ = auth_query;
|
|
||||||
return auth_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitStreamQuery(
|
|
||||||
MemgraphCypher::StreamQueryContext *ctx) {
|
|
||||||
CHECK(ctx->children.size() == 1)
|
|
||||||
<< "StreamQuery should have exactly one child!";
|
|
||||||
auto *stream_query = ctx->children[0]->accept(this).as<StreamQuery *>();
|
|
||||||
query_ = stream_query;
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitDumpQuery(
|
|
||||||
MemgraphCypher::DumpQueryContext *ctx) {
|
|
||||||
auto *dump_query = storage_->Create<DumpQuery>();
|
|
||||||
query_ = dump_query;
|
|
||||||
return dump_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitCypherUnion(
|
antlrcpp::Any CypherMainVisitor::visitCypherUnion(
|
||||||
MemgraphCypher::CypherUnionContext *ctx) {
|
MemgraphCypher::CypherUnionContext *ctx) {
|
||||||
bool distinct = !ctx->ALL();
|
bool distinct = !ctx->ALL();
|
||||||
@@ -346,382 +318,6 @@ antlrcpp::Any CypherMainVisitor::visitCreate(
|
|||||||
return create;
|
return create;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return std::string
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitUserOrRoleName(
|
|
||||||
MemgraphCypher::UserOrRoleNameContext *ctx) {
|
|
||||||
std::string value = ctx->symbolicName()->accept(this).as<std::string>();
|
|
||||||
const std::regex NAME_REGEX("[a-zA-Z0-9_.+-]+");
|
|
||||||
if (!std::regex_match(value, NAME_REGEX)) {
|
|
||||||
throw SyntaxException("Invalid user or role name.");
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitCreateRole(
|
|
||||||
MemgraphCypher::CreateRoleContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::CREATE_ROLE;
|
|
||||||
auth->role_ = ctx->role->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitDropRole(
|
|
||||||
MemgraphCypher::DropRoleContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::DROP_ROLE;
|
|
||||||
auth->role_ = ctx->role->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowRoles(
|
|
||||||
MemgraphCypher::ShowRolesContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SHOW_ROLES;
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitCreateUser(
|
|
||||||
MemgraphCypher::CreateUserContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::CREATE_USER;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
if (ctx->password) {
|
|
||||||
if (!ctx->password->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
|
|
||||||
throw SyntaxException("Password should be a string literal or null.");
|
|
||||||
}
|
|
||||||
auth->password_ = ctx->password->accept(this);
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitSetPassword(
|
|
||||||
MemgraphCypher::SetPasswordContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SET_PASSWORD;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
if (!ctx->password->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
|
|
||||||
throw SyntaxException("Password should be a string literal or null.");
|
|
||||||
}
|
|
||||||
auth->password_ = ctx->password->accept(this);
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitDropUser(
|
|
||||||
MemgraphCypher::DropUserContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::DROP_USER;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowUsers(
|
|
||||||
MemgraphCypher::ShowUsersContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SHOW_USERS;
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitSetRole(
|
|
||||||
MemgraphCypher::SetRoleContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SET_ROLE;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
auth->role_ = ctx->role->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitClearRole(
|
|
||||||
MemgraphCypher::ClearRoleContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::CLEAR_ROLE;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitGrantPrivilege(
|
|
||||||
MemgraphCypher::GrantPrivilegeContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::GRANT_PRIVILEGE;
|
|
||||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
|
||||||
if (ctx->privilegeList()) {
|
|
||||||
for (auto *privilege : ctx->privilegeList()->privilege()) {
|
|
||||||
auth->privileges_.push_back(privilege->accept(this));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* grant all privileges */
|
|
||||||
auth->privileges_ = kPrivilegesAll;
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitDenyPrivilege(
|
|
||||||
MemgraphCypher::DenyPrivilegeContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::DENY_PRIVILEGE;
|
|
||||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
|
||||||
if (ctx->privilegeList()) {
|
|
||||||
for (auto *privilege : ctx->privilegeList()->privilege()) {
|
|
||||||
auth->privileges_.push_back(privilege->accept(this));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* deny all privileges */
|
|
||||||
auth->privileges_ = kPrivilegesAll;
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitRevokePrivilege(
|
|
||||||
MemgraphCypher::RevokePrivilegeContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::REVOKE_PRIVILEGE;
|
|
||||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
|
||||||
if (ctx->privilegeList()) {
|
|
||||||
for (auto *privilege : ctx->privilegeList()->privilege()) {
|
|
||||||
auth->privileges_.push_back(privilege->accept(this));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* revoke all privileges */
|
|
||||||
auth->privileges_ = kPrivilegesAll;
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery::Privilege
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitPrivilege(
|
|
||||||
MemgraphCypher::PrivilegeContext *ctx) {
|
|
||||||
if (ctx->CREATE()) return AuthQuery::Privilege::CREATE;
|
|
||||||
if (ctx->DELETE()) return AuthQuery::Privilege::DELETE;
|
|
||||||
if (ctx->MATCH()) return AuthQuery::Privilege::MATCH;
|
|
||||||
if (ctx->MERGE()) return AuthQuery::Privilege::MERGE;
|
|
||||||
if (ctx->SET()) return AuthQuery::Privilege::SET;
|
|
||||||
if (ctx->REMOVE()) return AuthQuery::Privilege::REMOVE;
|
|
||||||
if (ctx->INDEX()) return AuthQuery::Privilege::INDEX;
|
|
||||||
if (ctx->STATS()) return AuthQuery::Privilege::STATS;
|
|
||||||
if (ctx->AUTH()) return AuthQuery::Privilege::AUTH;
|
|
||||||
if (ctx->STREAM()) return AuthQuery::Privilege::STREAM;
|
|
||||||
if (ctx->CONSTRAINT()) return AuthQuery::Privilege::CONSTRAINT;
|
|
||||||
LOG(FATAL) << "Should not get here - unknown privilege!";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowPrivileges(
|
|
||||||
MemgraphCypher::ShowPrivilegesContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SHOW_PRIVILEGES;
|
|
||||||
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowRoleForUser(
|
|
||||||
MemgraphCypher::ShowRoleForUserContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SHOW_ROLE_FOR_USER;
|
|
||||||
auth->user_ = ctx->user->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowUsersForRole(
|
|
||||||
MemgraphCypher::ShowUsersForRoleContext *ctx) {
|
|
||||||
AuthQuery *auth = storage_->Create<AuthQuery>();
|
|
||||||
auth->action_ = AuthQuery::Action::SHOW_USERS_FOR_ROLE;
|
|
||||||
auth->role_ = ctx->role->accept(this).as<std::string>();
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitCreateStream(
|
|
||||||
MemgraphCypher::CreateStreamContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::CREATE_STREAM;
|
|
||||||
stream_query->stream_name_ = ctx->streamName()->getText();
|
|
||||||
if (!ctx->streamUri->StringLiteral()) {
|
|
||||||
throw SyntaxException("Stream URI should be a string literal.");
|
|
||||||
}
|
|
||||||
stream_query->stream_uri_ = ctx->streamUri->accept(this);
|
|
||||||
if (!ctx->streamTopic->StringLiteral()) {
|
|
||||||
throw SyntaxException("Topic should be a string literal.");
|
|
||||||
}
|
|
||||||
stream_query->stream_topic_ = ctx->streamTopic->accept(this);
|
|
||||||
if (!ctx->transformUri->StringLiteral()) {
|
|
||||||
throw SyntaxException("Transform URI should be a string literal.");
|
|
||||||
}
|
|
||||||
stream_query->transform_uri_ = ctx->transformUri->accept(this);
|
|
||||||
if (ctx->batchIntervalOption()) {
|
|
||||||
stream_query->batch_interval_in_ms_ =
|
|
||||||
ctx->batchIntervalOption()->accept(this);
|
|
||||||
}
|
|
||||||
if (ctx->batchSizeOption()) {
|
|
||||||
stream_query->batch_size_ = ctx->batchSizeOption()->accept(this);
|
|
||||||
}
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Expression*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitBatchIntervalOption(
|
|
||||||
MemgraphCypher::BatchIntervalOptionContext *ctx) {
|
|
||||||
if (!ctx->literal()->numberLiteral() ||
|
|
||||||
!ctx->literal()->numberLiteral()->integerLiteral()) {
|
|
||||||
throw SyntaxException("Batch interval should be an integer.");
|
|
||||||
}
|
|
||||||
return ctx->literal()->accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Expression*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitBatchSizeOption(
|
|
||||||
MemgraphCypher::BatchSizeOptionContext *ctx) {
|
|
||||||
if (!ctx->literal()->numberLiteral() ||
|
|
||||||
!ctx->literal()->numberLiteral()->integerLiteral()) {
|
|
||||||
throw SyntaxException("Batch size should be an integer.");
|
|
||||||
}
|
|
||||||
return ctx->literal()->accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitDropStream(
|
|
||||||
MemgraphCypher::DropStreamContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::DROP_STREAM;
|
|
||||||
stream_query->stream_name_ = ctx->streamName()->getText();
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitShowStreams(
|
|
||||||
MemgraphCypher::ShowStreamsContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::SHOW_STREAMS;
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitStartStream(
|
|
||||||
MemgraphCypher::StartStreamContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::START_STREAM;
|
|
||||||
stream_query->stream_name_ = std::string(ctx->streamName()->getText());
|
|
||||||
if (ctx->limitBatchesOption()) {
|
|
||||||
stream_query->limit_batches_ = ctx->limitBatchesOption()->accept(this);
|
|
||||||
}
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitStopStream(
|
|
||||||
MemgraphCypher::StopStreamContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::STOP_STREAM;
|
|
||||||
stream_query->stream_name_ = std::string(ctx->streamName()->getText());
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Expression*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitLimitBatchesOption(
|
|
||||||
MemgraphCypher::LimitBatchesOptionContext *ctx) {
|
|
||||||
if (!ctx->literal()->numberLiteral() ||
|
|
||||||
!ctx->literal()->numberLiteral()->integerLiteral()) {
|
|
||||||
throw SyntaxException("Batch limit should be an integer.");
|
|
||||||
}
|
|
||||||
return ctx->literal()->accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitStartAllStreams(
|
|
||||||
MemgraphCypher::StartAllStreamsContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::START_ALL_STREAMS;
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitStopAllStreams(
|
|
||||||
MemgraphCypher::StopAllStreamsContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::STOP_ALL_STREAMS;
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitTestStream(
|
|
||||||
MemgraphCypher::TestStreamContext *ctx) {
|
|
||||||
auto *stream_query = storage_->Create<StreamQuery>();
|
|
||||||
stream_query->action_ = StreamQuery::Action::TEST_STREAM;
|
|
||||||
stream_query->stream_name_ = std::string(ctx->streamName()->getText());
|
|
||||||
if (ctx->limitBatchesOption()) {
|
|
||||||
stream_query->limit_batches_ = ctx->limitBatchesOption()->accept(this);
|
|
||||||
}
|
|
||||||
return stream_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
|
antlrcpp::Any CypherMainVisitor::visitCypherReturn(
|
||||||
MemgraphCypher::CypherReturnContext *ctx) {
|
MemgraphCypher::CypherReturnContext *ctx) {
|
||||||
auto *return_clause = storage_->Create<Return>();
|
auto *return_clause = storage_->Create<Return>();
|
||||||
|
|||||||
@@ -176,22 +176,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
antlrcpp::Any visitConstraintQuery(
|
antlrcpp::Any visitConstraintQuery(
|
||||||
MemgraphCypher::ConstraintQueryContext *ctx) override;
|
MemgraphCypher::ConstraintQueryContext *ctx) override;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitAuthQuery(MemgraphCypher::AuthQueryContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitStreamQuery(
|
|
||||||
MemgraphCypher::StreamQueryContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DumpQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitDumpQuery(MemgraphCypher::DumpQueryContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return CypherUnion*
|
* @return CypherUnion*
|
||||||
*/
|
*/
|
||||||
@@ -220,28 +204,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
*/
|
*/
|
||||||
antlrcpp::Any visitCreate(MemgraphCypher::CreateContext *ctx) override;
|
antlrcpp::Any visitCreate(MemgraphCypher::CreateContext *ctx) override;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return std::string
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitUserOrRoleName(
|
|
||||||
MemgraphCypher::UserOrRoleNameContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitCreateRole(
|
|
||||||
MemgraphCypher::CreateRoleContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitDropRole(MemgraphCypher::DropRoleContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowRoles(MemgraphCypher::ShowRolesContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return IndexQuery*
|
* @return IndexQuery*
|
||||||
*/
|
*/
|
||||||
@@ -253,136 +215,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
*/
|
*/
|
||||||
antlrcpp::Any visitDropIndex(MemgraphCypher::DropIndexContext *ctx) override;
|
antlrcpp::Any visitDropIndex(MemgraphCypher::DropIndexContext *ctx) override;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitCreateUser(
|
|
||||||
MemgraphCypher::CreateUserContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitSetPassword(
|
|
||||||
MemgraphCypher::SetPasswordContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitDropUser(MemgraphCypher::DropUserContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowUsers(MemgraphCypher::ShowUsersContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitSetRole(MemgraphCypher::SetRoleContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitClearRole(MemgraphCypher::ClearRoleContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitGrantPrivilege(
|
|
||||||
MemgraphCypher::GrantPrivilegeContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitDenyPrivilege(
|
|
||||||
MemgraphCypher::DenyPrivilegeContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitRevokePrivilege(
|
|
||||||
MemgraphCypher::RevokePrivilegeContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery::Privilege
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitPrivilege(MemgraphCypher::PrivilegeContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowPrivileges(
|
|
||||||
MemgraphCypher::ShowPrivilegesContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowRoleForUser(
|
|
||||||
MemgraphCypher::ShowRoleForUserContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AuthQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowUsersForRole(
|
|
||||||
MemgraphCypher::ShowUsersForRoleContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitCreateStream(
|
|
||||||
MemgraphCypher::CreateStreamContext *ctx) override;
|
|
||||||
|
|
||||||
antlrcpp::Any visitBatchIntervalOption(
|
|
||||||
MemgraphCypher::BatchIntervalOptionContext *ctx) override;
|
|
||||||
|
|
||||||
antlrcpp::Any visitBatchSizeOption(
|
|
||||||
MemgraphCypher::BatchSizeOptionContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitDropStream(
|
|
||||||
MemgraphCypher::DropStreamContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitShowStreams(
|
|
||||||
MemgraphCypher::ShowStreamsContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitStartStream(
|
|
||||||
MemgraphCypher::StartStreamContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitStopStream(
|
|
||||||
MemgraphCypher::StopStreamContext *ctx) override;
|
|
||||||
|
|
||||||
antlrcpp::Any visitLimitBatchesOption(
|
|
||||||
MemgraphCypher::LimitBatchesOptionContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitStartAllStreams(
|
|
||||||
MemgraphCypher::StartAllStreamsContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitStopAllStreams(
|
|
||||||
MemgraphCypher::StopAllStreamsContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return StreamQuery*
|
|
||||||
*/
|
|
||||||
antlrcpp::Any visitTestStream(
|
|
||||||
MemgraphCypher::TestStreamContext *ctx) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Return*
|
* @return Return*
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -46,9 +46,7 @@ indexInfo : INDEX INFO ;
|
|||||||
|
|
||||||
constraintInfo : CONSTRAINT INFO ;
|
constraintInfo : CONSTRAINT INFO ;
|
||||||
|
|
||||||
raftInfo : RAFT INFO ;
|
infoQuery : SHOW ( storageInfo | indexInfo | constraintInfo ) ;
|
||||||
|
|
||||||
infoQuery : SHOW ( storageInfo | indexInfo | constraintInfo | raftInfo ) ;
|
|
||||||
|
|
||||||
explainQuery : EXPLAIN cypherQuery ;
|
explainQuery : EXPLAIN cypherQuery ;
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ OPTIONAL : O P T I O N A L ;
|
|||||||
OR : O R ;
|
OR : O R ;
|
||||||
ORDER : O R D E R ;
|
ORDER : O R D E R ;
|
||||||
PROFILE : P R O F I L E ;
|
PROFILE : P R O F I L E ;
|
||||||
RAFT : R A F T ;
|
|
||||||
REDUCE : R E D U C E ;
|
REDUCE : R E D U C E ;
|
||||||
REMOVE : R E M O V E ;
|
REMOVE : R E M O V E ;
|
||||||
RETURN : R E T U R N ;
|
RETURN : R E T U R N ;
|
||||||
|
|||||||
@@ -5,149 +5,3 @@ parser grammar MemgraphCypher ;
|
|||||||
options { tokenVocab=MemgraphCypherLexer; }
|
options { tokenVocab=MemgraphCypherLexer; }
|
||||||
|
|
||||||
import Cypher ;
|
import Cypher ;
|
||||||
|
|
||||||
memgraphCypherKeyword : cypherKeyword
|
|
||||||
| ALTER
|
|
||||||
| AUTH
|
|
||||||
| BATCH
|
|
||||||
| BATCHES
|
|
||||||
| CLEAR
|
|
||||||
| DATA
|
|
||||||
| DATABASE
|
|
||||||
| DENY
|
|
||||||
| DROP
|
|
||||||
| DUMP
|
|
||||||
| FOR
|
|
||||||
| FROM
|
|
||||||
| GRANT
|
|
||||||
| IDENTIFIED
|
|
||||||
| INTERVAL
|
|
||||||
| K_TEST
|
|
||||||
| KAFKA
|
|
||||||
| LOAD
|
|
||||||
| PASSWORD
|
|
||||||
| PRIVILEGES
|
|
||||||
| REVOKE
|
|
||||||
| ROLE
|
|
||||||
| ROLES
|
|
||||||
| SIZE
|
|
||||||
| START
|
|
||||||
| STATS
|
|
||||||
| STOP
|
|
||||||
| STREAM
|
|
||||||
| STREAMS
|
|
||||||
| TO
|
|
||||||
| TOPIC
|
|
||||||
| TRANSFORM
|
|
||||||
| USER
|
|
||||||
| USERS
|
|
||||||
;
|
|
||||||
|
|
||||||
symbolicName : UnescapedSymbolicName
|
|
||||||
| EscapedSymbolicName
|
|
||||||
| memgraphCypherKeyword
|
|
||||||
;
|
|
||||||
|
|
||||||
query : cypherQuery
|
|
||||||
| indexQuery
|
|
||||||
| explainQuery
|
|
||||||
| profileQuery
|
|
||||||
| infoQuery
|
|
||||||
| constraintQuery
|
|
||||||
| authQuery
|
|
||||||
| streamQuery
|
|
||||||
| dumpQuery
|
|
||||||
;
|
|
||||||
|
|
||||||
authQuery : createRole
|
|
||||||
| dropRole
|
|
||||||
| showRoles
|
|
||||||
| createUser
|
|
||||||
| setPassword
|
|
||||||
| dropUser
|
|
||||||
| showUsers
|
|
||||||
| setRole
|
|
||||||
| clearRole
|
|
||||||
| grantPrivilege
|
|
||||||
| denyPrivilege
|
|
||||||
| revokePrivilege
|
|
||||||
| showPrivileges
|
|
||||||
| showRoleForUser
|
|
||||||
| showUsersForRole
|
|
||||||
;
|
|
||||||
|
|
||||||
userOrRoleName : symbolicName ;
|
|
||||||
|
|
||||||
createRole : CREATE ROLE role=userOrRoleName ;
|
|
||||||
|
|
||||||
dropRole : DROP ROLE role=userOrRoleName ;
|
|
||||||
|
|
||||||
showRoles : SHOW ROLES ;
|
|
||||||
|
|
||||||
createUser : CREATE USER user=userOrRoleName
|
|
||||||
( IDENTIFIED BY password=literal )? ;
|
|
||||||
|
|
||||||
setPassword : SET PASSWORD FOR user=userOrRoleName TO password=literal;
|
|
||||||
|
|
||||||
dropUser : DROP USER user=userOrRoleName ;
|
|
||||||
|
|
||||||
showUsers : SHOW USERS ;
|
|
||||||
|
|
||||||
setRole : SET ROLE FOR user=userOrRoleName TO role=userOrRoleName;
|
|
||||||
|
|
||||||
clearRole : CLEAR ROLE FOR user=userOrRoleName ;
|
|
||||||
|
|
||||||
grantPrivilege : GRANT ( ALL PRIVILEGES | privileges=privilegeList ) TO userOrRole=userOrRoleName ;
|
|
||||||
|
|
||||||
denyPrivilege : DENY ( ALL PRIVILEGES | privileges=privilegeList ) TO userOrRole=userOrRoleName ;
|
|
||||||
|
|
||||||
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=privilegeList ) FROM userOrRole=userOrRoleName ;
|
|
||||||
|
|
||||||
privilege : CREATE | DELETE | MATCH | MERGE | SET
|
|
||||||
| REMOVE | INDEX | STATS | AUTH | STREAM | CONSTRAINT | DUMP ;
|
|
||||||
|
|
||||||
privilegeList : privilege ( ',' privilege )* ;
|
|
||||||
|
|
||||||
showPrivileges : SHOW PRIVILEGES FOR userOrRole=userOrRoleName ;
|
|
||||||
|
|
||||||
showRoleForUser : SHOW ROLE FOR user=userOrRoleName ;
|
|
||||||
|
|
||||||
showUsersForRole : SHOW USERS FOR role=userOrRoleName ;
|
|
||||||
|
|
||||||
streamQuery : createStream
|
|
||||||
| dropStream
|
|
||||||
| showStreams
|
|
||||||
| startStream
|
|
||||||
| stopStream
|
|
||||||
| startAllStreams
|
|
||||||
| stopAllStreams
|
|
||||||
| testStream
|
|
||||||
;
|
|
||||||
|
|
||||||
streamName : symbolicName ;
|
|
||||||
|
|
||||||
createStream : CREATE STREAM streamName AS LOAD DATA KAFKA
|
|
||||||
streamUri=literal WITH TOPIC streamTopic=literal WITH TRANSFORM
|
|
||||||
transformUri=literal ( batchIntervalOption )? ( batchSizeOption )? ;
|
|
||||||
|
|
||||||
batchIntervalOption : BATCH INTERVAL literal ;
|
|
||||||
|
|
||||||
batchSizeOption : BATCH SIZE literal ;
|
|
||||||
|
|
||||||
dropStream : DROP STREAM streamName ;
|
|
||||||
|
|
||||||
showStreams : SHOW STREAMS ;
|
|
||||||
|
|
||||||
startStream : START STREAM streamName ( limitBatchesOption )? ;
|
|
||||||
|
|
||||||
stopStream : STOP STREAM streamName ;
|
|
||||||
|
|
||||||
limitBatchesOption : LIMIT limitBatches=literal BATCHES ;
|
|
||||||
|
|
||||||
startAllStreams : START ALL STREAMS ;
|
|
||||||
|
|
||||||
stopAllStreams : STOP ALL STREAMS ;
|
|
||||||
|
|
||||||
testStream : K_TEST STREAM streamName ( limitBatchesOption )? ;
|
|
||||||
|
|
||||||
dumpQuery: DUMP DATABASE ;
|
|
||||||
|
|||||||
@@ -9,39 +9,3 @@
|
|||||||
lexer grammar MemgraphCypherLexer ;
|
lexer grammar MemgraphCypherLexer ;
|
||||||
|
|
||||||
import CypherLexer ;
|
import CypherLexer ;
|
||||||
|
|
||||||
ALTER : A L T E R ;
|
|
||||||
AUTH : A U T H ;
|
|
||||||
BATCH : B A T C H ;
|
|
||||||
BATCHES : B A T C H E S ;
|
|
||||||
CLEAR : C L E A R ;
|
|
||||||
DATA : D A T A ;
|
|
||||||
DATABASE : D A T A B A S E ;
|
|
||||||
DENY : D E N Y ;
|
|
||||||
DROP : D R O P ;
|
|
||||||
DUMP : D U M P ;
|
|
||||||
FOR : F O R ;
|
|
||||||
FROM : F R O M ;
|
|
||||||
GRANT : G R A N T ;
|
|
||||||
GRANTS : G R A N T S ;
|
|
||||||
IDENTIFIED : I D E N T I F I E D ;
|
|
||||||
INTERVAL : I N T E R V A L ;
|
|
||||||
K_TEST : T E S T ;
|
|
||||||
KAFKA : K A F K A ;
|
|
||||||
LOAD : L O A D ;
|
|
||||||
PASSWORD : P A S S W O R D ;
|
|
||||||
PRIVILEGES : P R I V I L E G E S ;
|
|
||||||
REVOKE : R E V O K E ;
|
|
||||||
ROLE : R O L E ;
|
|
||||||
ROLES : R O L E S ;
|
|
||||||
SIZE : S I Z E ;
|
|
||||||
START : S T A R T ;
|
|
||||||
STATS : S T A T S ;
|
|
||||||
STOP : S T O P ;
|
|
||||||
STREAM : S T R E A M ;
|
|
||||||
STREAMS : S T R E A M S ;
|
|
||||||
TO : T O ;
|
|
||||||
TOPIC : T O P I C ;
|
|
||||||
TRANSFORM : T R A N S F O R M ;
|
|
||||||
USER : U S E R ;
|
|
||||||
USERS : U S E R S ;
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,26 +4,15 @@
|
|||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "auth/auth.hpp"
|
|
||||||
#ifdef MG_SINGLE_NODE
|
|
||||||
#include "database/single_node/dump.hpp"
|
|
||||||
#endif
|
|
||||||
#include "glue/auth.hpp"
|
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
#include "integrations/kafka/exceptions.hpp"
|
|
||||||
#include "integrations/kafka/streams.hpp"
|
|
||||||
#include "query/exceptions.hpp"
|
#include "query/exceptions.hpp"
|
||||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||||
#include "query/frontend/opencypher/parser.hpp"
|
#include "query/frontend/opencypher/parser.hpp"
|
||||||
#include "query/frontend/semantic/required_privileges.hpp"
|
|
||||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||||
#include "query/interpret/eval.hpp"
|
#include "query/interpret/eval.hpp"
|
||||||
#include "query/plan/planner.hpp"
|
#include "query/plan/planner.hpp"
|
||||||
#include "query/plan/profile.hpp"
|
#include "query/plan/profile.hpp"
|
||||||
#include "query/plan/vertex_count_cache.hpp"
|
#include "query/plan/vertex_count_cache.hpp"
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
#include "raft/exceptions.hpp"
|
|
||||||
#endif
|
|
||||||
#include "utils/exceptions.hpp"
|
#include "utils/exceptions.hpp"
|
||||||
#include "utils/flag_validation.hpp"
|
#include "utils/flag_validation.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
@@ -37,40 +26,6 @@ DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60,
|
|||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
#ifdef MG_SINGLE_NODE
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class DumpClosure final {
|
|
||||||
public:
|
|
||||||
explicit DumpClosure(database::GraphDbAccessor *dba) : dump_generator_(dba) {}
|
|
||||||
|
|
||||||
// Please note that this copy constructor actually moves the other object. We
|
|
||||||
// want this because lambdas are not movable, i.e. its move constructor
|
|
||||||
// actually copies the lambda.
|
|
||||||
DumpClosure(const DumpClosure &other)
|
|
||||||
: dump_generator_(std::move(other.dump_generator_)) {}
|
|
||||||
|
|
||||||
DumpClosure(DumpClosure &&other) = default;
|
|
||||||
DumpClosure &operator=(const DumpClosure &other) = delete;
|
|
||||||
DumpClosure &operator=(DumpClosure &&other) = delete;
|
|
||||||
~DumpClosure() {}
|
|
||||||
|
|
||||||
std::optional<std::vector<TypedValue>> operator()(Frame *frame,
|
|
||||||
ExecutionContext *context) {
|
|
||||||
std::ostringstream oss;
|
|
||||||
if (dump_generator_.NextQuery(&oss)) {
|
|
||||||
return std::make_optional(std::vector<TypedValue>{TypedValue(oss.str())});
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable database::CypherDumpGenerator dump_generator_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class SingleNodeLogicalPlan final : public LogicalPlan {
|
class SingleNodeLogicalPlan final : public LogicalPlan {
|
||||||
public:
|
public:
|
||||||
SingleNodeLogicalPlan(std::unique_ptr<plan::LogicalOperator> root,
|
SingleNodeLogicalPlan(std::unique_ptr<plan::LogicalOperator> root,
|
||||||
@@ -118,466 +73,6 @@ TypedValue EvaluateOptionalExpression(Expression *expression,
|
|||||||
return expression ? expression->Accept(*eval) : TypedValue();
|
return expression ? expression->Accept(*eval) : TypedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
|
|
||||||
const Parameters ¶meters,
|
|
||||||
database::GraphDbAccessor *db_accessor) {
|
|
||||||
// Empty frame for evaluation of password expression. This is OK since
|
|
||||||
// password should be either null or string literal and it's evaluation
|
|
||||||
// should not depend on frame.
|
|
||||||
Frame frame(0);
|
|
||||||
SymbolTable symbol_table;
|
|
||||||
EvaluationContext evaluation_context;
|
|
||||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
|
||||||
// the argument to Callback.
|
|
||||||
evaluation_context.timestamp =
|
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
|
||||||
.count();
|
|
||||||
evaluation_context.parameters = parameters;
|
|
||||||
ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context,
|
|
||||||
db_accessor, GraphView::OLD);
|
|
||||||
|
|
||||||
AuthQuery::Action action = auth_query->action_;
|
|
||||||
std::string username = auth_query->user_;
|
|
||||||
std::string rolename = auth_query->role_;
|
|
||||||
std::string user_or_role = auth_query->user_or_role_;
|
|
||||||
std::vector<AuthQuery::Privilege> privileges = auth_query->privileges_;
|
|
||||||
auto password = EvaluateOptionalExpression(auth_query->password_, &evaluator);
|
|
||||||
|
|
||||||
Callback callback;
|
|
||||||
|
|
||||||
switch (auth_query->action_) {
|
|
||||||
case AuthQuery::Action::CREATE_USER:
|
|
||||||
callback.fn = [auth, username, password] {
|
|
||||||
CHECK(password.IsString() || password.IsNull());
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->AddUser(
|
|
||||||
username,
|
|
||||||
password.IsString()
|
|
||||||
? std::make_optional(std::string(password.ValueString()))
|
|
||||||
: std::nullopt);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User or role '{}' already exists.",
|
|
||||||
username);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::DROP_USER:
|
|
||||||
callback.fn = [auth, username] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User '{}' doesn't exist.", username);
|
|
||||||
}
|
|
||||||
if (!auth->RemoveUser(username)) {
|
|
||||||
throw QueryRuntimeException("Couldn't remove user '{}'.", username);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SET_PASSWORD:
|
|
||||||
callback.fn = [auth, username, password] {
|
|
||||||
CHECK(password.IsString() || password.IsNull());
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User '{}' doesn't exist.", username);
|
|
||||||
}
|
|
||||||
user->UpdatePassword(
|
|
||||||
password.IsString()
|
|
||||||
? std::make_optional(std::string(password.ValueString()))
|
|
||||||
: std::nullopt);
|
|
||||||
auth->SaveUser(*user);
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::CREATE_ROLE:
|
|
||||||
callback.fn = [auth, rolename] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto role = auth->AddRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
throw QueryRuntimeException("User or role '{}' already exists.",
|
|
||||||
rolename);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::DROP_ROLE:
|
|
||||||
callback.fn = [auth, rolename] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto role = auth->GetRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
throw QueryRuntimeException("Role '{}' doesn't exist.", rolename);
|
|
||||||
}
|
|
||||||
if (!auth->RemoveRole(rolename)) {
|
|
||||||
throw QueryRuntimeException("Couldn't remove role '{}'.", rolename);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SHOW_USERS:
|
|
||||||
callback.header = {"user"};
|
|
||||||
callback.fn = [auth] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
std::vector<std::vector<TypedValue>> users;
|
|
||||||
for (const auto &user : auth->AllUsers()) {
|
|
||||||
users.push_back({TypedValue(user.username())});
|
|
||||||
}
|
|
||||||
return users;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SHOW_ROLES:
|
|
||||||
callback.header = {"role"};
|
|
||||||
callback.fn = [auth] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
std::vector<std::vector<TypedValue>> roles;
|
|
||||||
for (const auto &role : auth->AllRoles()) {
|
|
||||||
roles.push_back({TypedValue(role.rolename())});
|
|
||||||
}
|
|
||||||
return roles;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SET_ROLE:
|
|
||||||
callback.fn = [auth, username, rolename] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
|
||||||
}
|
|
||||||
auto role = auth->GetRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
throw QueryRuntimeException("Role '{}' doesn't exist .", rolename);
|
|
||||||
}
|
|
||||||
if (user->role()) {
|
|
||||||
throw QueryRuntimeException(
|
|
||||||
"User '{}' is already a member of role '{}'.", username,
|
|
||||||
user->role()->rolename());
|
|
||||||
}
|
|
||||||
user->SetRole(*role);
|
|
||||||
auth->SaveUser(*user);
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::CLEAR_ROLE:
|
|
||||||
callback.fn = [auth, username] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
|
||||||
}
|
|
||||||
user->ClearRole();
|
|
||||||
auth->SaveUser(*user);
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::GRANT_PRIVILEGE:
|
|
||||||
case AuthQuery::Action::DENY_PRIVILEGE:
|
|
||||||
case AuthQuery::Action::REVOKE_PRIVILEGE: {
|
|
||||||
callback.fn = [auth, user_or_role, action, privileges] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
std::vector<auth::Permission> permissions;
|
|
||||||
for (const auto &privilege : privileges) {
|
|
||||||
permissions.push_back(glue::PrivilegeToPermission(privilege));
|
|
||||||
}
|
|
||||||
auto user = auth->GetUser(user_or_role);
|
|
||||||
auto role = auth->GetRole(user_or_role);
|
|
||||||
if (!user && !role) {
|
|
||||||
throw QueryRuntimeException("User or role '{}' doesn't exist.",
|
|
||||||
user_or_role);
|
|
||||||
}
|
|
||||||
if (user) {
|
|
||||||
for (const auto &permission : permissions) {
|
|
||||||
// TODO (mferencevic): should we first check that the privilege
|
|
||||||
// is granted/denied/revoked before unconditionally
|
|
||||||
// granting/denying/revoking it?
|
|
||||||
if (action == AuthQuery::Action::GRANT_PRIVILEGE) {
|
|
||||||
user->permissions().Grant(permission);
|
|
||||||
} else if (action == AuthQuery::Action::DENY_PRIVILEGE) {
|
|
||||||
user->permissions().Deny(permission);
|
|
||||||
} else {
|
|
||||||
user->permissions().Revoke(permission);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auth->SaveUser(*user);
|
|
||||||
} else {
|
|
||||||
for (const auto &permission : permissions) {
|
|
||||||
// TODO (mferencevic): should we first check that the privilege
|
|
||||||
// is granted/denied/revoked before unconditionally
|
|
||||||
// granting/denying/revoking it?
|
|
||||||
if (action == AuthQuery::Action::GRANT_PRIVILEGE) {
|
|
||||||
role->permissions().Grant(permission);
|
|
||||||
} else if (action == AuthQuery::Action::DENY_PRIVILEGE) {
|
|
||||||
role->permissions().Deny(permission);
|
|
||||||
} else {
|
|
||||||
role->permissions().Revoke(permission);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auth->SaveRole(*role);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
case AuthQuery::Action::SHOW_PRIVILEGES:
|
|
||||||
callback.header = {"privilege", "effective", "description"};
|
|
||||||
callback.fn = [auth, user_or_role] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
std::vector<std::vector<TypedValue>> grants;
|
|
||||||
auto user = auth->GetUser(user_or_role);
|
|
||||||
auto role = auth->GetRole(user_or_role);
|
|
||||||
if (!user && !role) {
|
|
||||||
throw QueryRuntimeException("User or role '{}' doesn't exist.",
|
|
||||||
user_or_role);
|
|
||||||
}
|
|
||||||
if (user) {
|
|
||||||
const auto &permissions = user->GetPermissions();
|
|
||||||
for (const auto &privilege : kPrivilegesAll) {
|
|
||||||
auto permission = glue::PrivilegeToPermission(privilege);
|
|
||||||
auto effective = permissions.Has(permission);
|
|
||||||
if (permissions.Has(permission) != auth::PermissionLevel::NEUTRAL) {
|
|
||||||
std::vector<std::string> description;
|
|
||||||
auto user_level = user->permissions().Has(permission);
|
|
||||||
if (user_level == auth::PermissionLevel::GRANT) {
|
|
||||||
description.push_back("GRANTED TO USER");
|
|
||||||
} else if (user_level == auth::PermissionLevel::DENY) {
|
|
||||||
description.push_back("DENIED TO USER");
|
|
||||||
}
|
|
||||||
if (user->role()) {
|
|
||||||
auto role_level = user->role()->permissions().Has(permission);
|
|
||||||
if (role_level == auth::PermissionLevel::GRANT) {
|
|
||||||
description.push_back("GRANTED TO ROLE");
|
|
||||||
} else if (role_level == auth::PermissionLevel::DENY) {
|
|
||||||
description.push_back("DENIED TO ROLE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
grants.push_back(
|
|
||||||
{TypedValue(auth::PermissionToString(permission)),
|
|
||||||
TypedValue(auth::PermissionLevelToString(effective)),
|
|
||||||
TypedValue(utils::Join(description, ", "))});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto &permissions = role->permissions();
|
|
||||||
for (const auto &privilege : kPrivilegesAll) {
|
|
||||||
auto permission = glue::PrivilegeToPermission(privilege);
|
|
||||||
auto effective = permissions.Has(permission);
|
|
||||||
if (effective != auth::PermissionLevel::NEUTRAL) {
|
|
||||||
std::string description;
|
|
||||||
if (effective == auth::PermissionLevel::GRANT) {
|
|
||||||
description = "GRANTED TO ROLE";
|
|
||||||
} else if (effective == auth::PermissionLevel::DENY) {
|
|
||||||
description = "DENIED TO ROLE";
|
|
||||||
}
|
|
||||||
grants.push_back(
|
|
||||||
{TypedValue(auth::PermissionToString(permission)),
|
|
||||||
TypedValue(auth::PermissionLevelToString(effective)),
|
|
||||||
TypedValue(description)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return grants;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SHOW_ROLE_FOR_USER:
|
|
||||||
callback.header = {"role"};
|
|
||||||
callback.fn = [auth, username] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto user = auth->GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>{std::vector<TypedValue>{
|
|
||||||
TypedValue(user->role() ? user->role()->rolename() : "null")}};
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case AuthQuery::Action::SHOW_USERS_FOR_ROLE:
|
|
||||||
callback.header = {"users"};
|
|
||||||
callback.fn = [auth, rolename] {
|
|
||||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
|
||||||
auto role = auth->GetRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
throw QueryRuntimeException("Role '{}' doesn't exist.", rolename);
|
|
||||||
}
|
|
||||||
std::vector<std::vector<TypedValue>> users;
|
|
||||||
for (const auto &user : auth->AllUsersForRole(rolename)) {
|
|
||||||
users.emplace_back(
|
|
||||||
std::vector<TypedValue>{TypedValue(user.username())});
|
|
||||||
}
|
|
||||||
return users;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Callback HandleStreamQuery(StreamQuery *stream_query,
|
|
||||||
integrations::kafka::Streams *streams,
|
|
||||||
const Parameters ¶meters,
|
|
||||||
database::GraphDbAccessor *db_accessor) {
|
|
||||||
// Empty frame and symbol table for evaluation of expressions. This is OK
|
|
||||||
// since all expressions should be literals or parameter lookups.
|
|
||||||
Frame frame(0);
|
|
||||||
SymbolTable symbol_table;
|
|
||||||
EvaluationContext evaluation_context;
|
|
||||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
|
||||||
// the argument to Callback.
|
|
||||||
evaluation_context.timestamp =
|
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
|
||||||
.count();
|
|
||||||
evaluation_context.parameters = parameters;
|
|
||||||
ExpressionEvaluator eval(&frame, symbol_table, evaluation_context,
|
|
||||||
db_accessor, GraphView::OLD);
|
|
||||||
|
|
||||||
std::string stream_name = stream_query->stream_name_;
|
|
||||||
auto stream_uri =
|
|
||||||
EvaluateOptionalExpression(stream_query->stream_uri_, &eval);
|
|
||||||
auto stream_topic =
|
|
||||||
EvaluateOptionalExpression(stream_query->stream_topic_, &eval);
|
|
||||||
auto transform_uri =
|
|
||||||
EvaluateOptionalExpression(stream_query->transform_uri_, &eval);
|
|
||||||
auto batch_interval_in_ms =
|
|
||||||
EvaluateOptionalExpression(stream_query->batch_interval_in_ms_, &eval);
|
|
||||||
auto batch_size =
|
|
||||||
EvaluateOptionalExpression(stream_query->batch_size_, &eval);
|
|
||||||
auto limit_batches =
|
|
||||||
EvaluateOptionalExpression(stream_query->limit_batches_, &eval);
|
|
||||||
|
|
||||||
Callback callback;
|
|
||||||
|
|
||||||
switch (stream_query->action_) {
|
|
||||||
case StreamQuery::Action::CREATE_STREAM:
|
|
||||||
callback.fn = [streams, stream_name, stream_uri, stream_topic,
|
|
||||||
transform_uri, batch_interval_in_ms, batch_size] {
|
|
||||||
CHECK(stream_uri.IsString());
|
|
||||||
CHECK(stream_topic.IsString());
|
|
||||||
CHECK(transform_uri.IsString());
|
|
||||||
CHECK(batch_interval_in_ms.IsInt() || batch_interval_in_ms.IsNull());
|
|
||||||
CHECK(batch_size.IsInt() || batch_size.IsNull());
|
|
||||||
|
|
||||||
integrations::kafka::StreamInfo info;
|
|
||||||
info.stream_name = stream_name;
|
|
||||||
|
|
||||||
info.stream_uri = stream_uri.ValueString();
|
|
||||||
info.stream_topic = stream_topic.ValueString();
|
|
||||||
info.transform_uri = transform_uri.ValueString();
|
|
||||||
info.batch_interval_in_ms =
|
|
||||||
batch_interval_in_ms.IsInt()
|
|
||||||
? std::make_optional(batch_interval_in_ms.ValueInt())
|
|
||||||
: std::nullopt;
|
|
||||||
info.batch_size = batch_size.IsInt()
|
|
||||||
? std::make_optional(batch_size.ValueInt())
|
|
||||||
: std::nullopt;
|
|
||||||
|
|
||||||
try {
|
|
||||||
streams->Create(info);
|
|
||||||
} catch (const integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::DROP_STREAM:
|
|
||||||
callback.fn = [streams, stream_name] {
|
|
||||||
try {
|
|
||||||
streams->Drop(stream_name);
|
|
||||||
} catch (const integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::SHOW_STREAMS:
|
|
||||||
callback.header = {"name", "uri", "topic", "transform", "status"};
|
|
||||||
callback.fn = [streams] {
|
|
||||||
std::vector<std::vector<TypedValue>> status;
|
|
||||||
for (const auto &stream : streams->Show()) {
|
|
||||||
status.push_back(std::vector<TypedValue>{
|
|
||||||
TypedValue(stream.stream_name), TypedValue(stream.stream_uri),
|
|
||||||
TypedValue(stream.stream_topic), TypedValue(stream.transform_uri),
|
|
||||||
TypedValue(stream.stream_status)});
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::START_STREAM:
|
|
||||||
callback.fn = [streams, stream_name, limit_batches] {
|
|
||||||
CHECK(limit_batches.IsInt() || limit_batches.IsNull());
|
|
||||||
|
|
||||||
try {
|
|
||||||
streams->Start(stream_name,
|
|
||||||
limit_batches.IsInt()
|
|
||||||
? std::make_optional(limit_batches.ValueInt())
|
|
||||||
: std::nullopt);
|
|
||||||
} catch (integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::STOP_STREAM:
|
|
||||||
callback.fn = [streams, stream_name] {
|
|
||||||
try {
|
|
||||||
streams->Stop(stream_name);
|
|
||||||
} catch (integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::START_ALL_STREAMS:
|
|
||||||
callback.fn = [streams] {
|
|
||||||
try {
|
|
||||||
streams->StartAll();
|
|
||||||
} catch (integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::STOP_ALL_STREAMS:
|
|
||||||
callback.fn = [streams] {
|
|
||||||
try {
|
|
||||||
streams->StopAll();
|
|
||||||
} catch (integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return std::vector<std::vector<TypedValue>>();
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
case StreamQuery::Action::TEST_STREAM:
|
|
||||||
callback.header = {"query", "params"};
|
|
||||||
callback.fn = [streams, stream_name, limit_batches] {
|
|
||||||
CHECK(limit_batches.IsInt() || limit_batches.IsNull());
|
|
||||||
|
|
||||||
std::vector<std::vector<TypedValue>> rows;
|
|
||||||
try {
|
|
||||||
auto results = streams->Test(
|
|
||||||
stream_name, limit_batches.IsInt()
|
|
||||||
? std::make_optional(limit_batches.ValueInt())
|
|
||||||
: std::nullopt);
|
|
||||||
for (const auto &result : results) {
|
|
||||||
std::map<std::string, TypedValue> params;
|
|
||||||
for (const auto ¶m : result.second) {
|
|
||||||
params.emplace(param.first, glue::ToTypedValue(param.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.emplace_back(std::vector<TypedValue>{TypedValue(result.first),
|
|
||||||
TypedValue(params)});
|
|
||||||
}
|
|
||||||
} catch (integrations::kafka::KafkaStreamException &e) {
|
|
||||||
throw QueryRuntimeException(e.what());
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
};
|
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Callback HandleIndexQuery(IndexQuery *index_query,
|
Callback HandleIndexQuery(IndexQuery *index_query,
|
||||||
std::function<void()> invalidate_plan_cache,
|
std::function<void()> invalidate_plan_cache,
|
||||||
database::GraphDbAccessor *db_accessor) {
|
database::GraphDbAccessor *db_accessor) {
|
||||||
@@ -631,7 +126,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
Callback callback;
|
Callback callback;
|
||||||
switch (info_query->info_type_) {
|
switch (info_query->info_type_) {
|
||||||
case InfoQuery::InfoType::STORAGE:
|
case InfoQuery::InfoType::STORAGE:
|
||||||
#if defined(MG_SINGLE_NODE)
|
|
||||||
callback.header = {"storage info", "value"};
|
callback.header = {"storage info", "value"};
|
||||||
callback.fn = [db_accessor] {
|
callback.fn = [db_accessor] {
|
||||||
auto info = db_accessor->StorageInfo();
|
auto info = db_accessor->StorageInfo();
|
||||||
@@ -642,24 +136,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
#elif defined(MG_SINGLE_NODE_HA)
|
|
||||||
callback.header = {"server id", "storage info", "value"};
|
|
||||||
callback.fn = [db_accessor] {
|
|
||||||
auto info = db_accessor->StorageInfo();
|
|
||||||
std::vector<std::vector<TypedValue>> results;
|
|
||||||
results.reserve(info.size());
|
|
||||||
for (const auto &peer_info : info) {
|
|
||||||
for (const auto &pair : peer_info.second) {
|
|
||||||
results.push_back({TypedValue(peer_info.first),
|
|
||||||
TypedValue(pair.first),
|
|
||||||
TypedValue(pair.second)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
throw utils::NotYetImplemented("storage info");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case InfoQuery::InfoType::INDEX:
|
case InfoQuery::InfoType::INDEX:
|
||||||
callback.header = {"created index"};
|
callback.header = {"created index"};
|
||||||
@@ -674,7 +150,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case InfoQuery::InfoType::CONSTRAINT:
|
case InfoQuery::InfoType::CONSTRAINT:
|
||||||
#if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA)
|
|
||||||
callback.header = {"constraint type", "label", "properties"};
|
callback.header = {"constraint type", "label", "properties"};
|
||||||
callback.fn = [db_accessor] {
|
callback.fn = [db_accessor] {
|
||||||
std::vector<std::vector<TypedValue>> results;
|
std::vector<std::vector<TypedValue>> results;
|
||||||
@@ -693,27 +168,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
#else
|
|
||||||
throw utils::NotYetImplemented("constraints info");
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case InfoQuery::InfoType::RAFT:
|
|
||||||
#if defined(MG_SINGLE_NODE_HA)
|
|
||||||
callback.header = {"info", "value"};
|
|
||||||
callback.fn = [db_accessor] {
|
|
||||||
std::vector<std::vector<TypedValue>> results(
|
|
||||||
{{TypedValue("is_leader"),
|
|
||||||
TypedValue(db_accessor->raft()->IsLeader())},
|
|
||||||
{TypedValue("term_id"), TypedValue(static_cast<int64_t>(
|
|
||||||
db_accessor->raft()->TermId()))}});
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
// It is critical to abort this query because it can be executed on
|
|
||||||
// machines that aren't the leader.
|
|
||||||
callback.should_abort_query = true;
|
|
||||||
#else
|
|
||||||
throw utils::NotYetImplemented("raft info");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return callback;
|
return callback;
|
||||||
@@ -721,7 +175,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
|
|
||||||
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
||||||
database::GraphDbAccessor *db_accessor) {
|
database::GraphDbAccessor *db_accessor) {
|
||||||
#if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA)
|
|
||||||
std::vector<storage::Property> properties;
|
std::vector<storage::Property> properties;
|
||||||
auto label = db_accessor->Label(constraint_query->constraint_.label.name);
|
auto label = db_accessor->Label(constraint_query->constraint_.label.name);
|
||||||
properties.reserve(constraint_query->constraint_.properties.size());
|
properties.reserve(constraint_query->constraint_.properties.size());
|
||||||
@@ -773,9 +226,6 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
|||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
return callback;
|
return callback;
|
||||||
#else
|
|
||||||
throw utils::NotYetImplemented("Constraints");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
|
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
|
||||||
@@ -783,7 +233,7 @@ Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
|
|||||||
Interpreter::Results Interpreter::operator()(
|
Interpreter::Results Interpreter::operator()(
|
||||||
const std::string &query_string, database::GraphDbAccessor &db_accessor,
|
const std::string &query_string, database::GraphDbAccessor &db_accessor,
|
||||||
const std::map<std::string, PropertyValue> ¶ms,
|
const std::map<std::string, PropertyValue> ¶ms,
|
||||||
bool in_explicit_transaction) {
|
bool in_explicit_transaction, utils::MemoryResource *execution_memory) {
|
||||||
AstStorage ast_storage;
|
AstStorage ast_storage;
|
||||||
Parameters parameters;
|
Parameters parameters;
|
||||||
std::map<std::string, TypedValue> summary;
|
std::map<std::string, TypedValue> summary;
|
||||||
@@ -809,17 +259,6 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
// we must ensure it lives during the whole interpretation.
|
// we must ensure it lives during the whole interpretation.
|
||||||
std::shared_ptr<CachedPlan> plan{nullptr};
|
std::shared_ptr<CachedPlan> plan{nullptr};
|
||||||
|
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
{
|
|
||||||
InfoQuery *info_query = nullptr;
|
|
||||||
if (!db_accessor.raft()->IsLeader() &&
|
|
||||||
(!(info_query = utils::Downcast<InfoQuery>(parsed_query.query)) ||
|
|
||||||
info_query->info_type_ != InfoQuery::InfoType::RAFT)) {
|
|
||||||
throw raft::CantExecuteQueries();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query)) {
|
if (auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query)) {
|
||||||
plan = CypherQueryToPlan(stripped_query.hash(), cypher_query,
|
plan = CypherQueryToPlan(stripped_query.hash(), cypher_query,
|
||||||
std::move(ast_storage), parameters, &db_accessor);
|
std::move(ast_storage), parameters, &db_accessor);
|
||||||
@@ -839,8 +278,9 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
.first);
|
.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Results(&db_accessor, parameters, plan, output_symbols, header,
|
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
|
||||||
summary, parsed_query.required_privileges);
|
std::move(header), std::move(summary),
|
||||||
|
execution_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils::IsSubtype(*parsed_query.query, ExplainQuery::kType)) {
|
if (utils::IsSubtype(*parsed_query.query, ExplainQuery::kType)) {
|
||||||
@@ -909,8 +349,9 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
|
|
||||||
std::vector<std::string> header{query_plan_symbol.name()};
|
std::vector<std::string> header{query_plan_symbol.name()};
|
||||||
|
|
||||||
return Results(&db_accessor, parameters, plan, output_symbols, header,
|
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
|
||||||
summary, parsed_query.required_privileges);
|
std::move(header), std::move(summary),
|
||||||
|
execution_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils::IsSubtype(*parsed_query.query, ProfileQuery::kType)) {
|
if (utils::IsSubtype(*parsed_query.query, ProfileQuery::kType)) {
|
||||||
@@ -990,37 +431,12 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
auto planning_time = planning_timer.Elapsed();
|
auto planning_time = planning_timer.Elapsed();
|
||||||
summary["planning_time"] = planning_time.count();
|
summary["planning_time"] = planning_time.count();
|
||||||
|
|
||||||
return Results(&db_accessor, parameters, plan, output_symbols, header,
|
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
|
||||||
summary, parsed_query.required_privileges,
|
std::move(header), std::move(summary),
|
||||||
|
execution_memory,
|
||||||
/* is_profile_query */ true, /* should_abort_query */ true);
|
/* is_profile_query */ true, /* should_abort_query */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto *dump_query = utils::Downcast<DumpQuery>(parsed_query.query)) {
|
|
||||||
#ifdef MG_SINGLE_NODE
|
|
||||||
database::CypherDumpGenerator dump(&db_accessor);
|
|
||||||
|
|
||||||
SymbolTable symbol_table;
|
|
||||||
auto query_symbol = symbol_table.CreateSymbol("QUERY", false);
|
|
||||||
|
|
||||||
std::vector<Symbol> output_symbols = {query_symbol};
|
|
||||||
std::vector<std::string> header = {query_symbol.name()};
|
|
||||||
|
|
||||||
auto output_plan = std::make_unique<plan::OutputTableStream>(
|
|
||||||
output_symbols, DumpClosure(&db_accessor));
|
|
||||||
plan = std::make_shared<CachedPlan>(std::make_unique<SingleNodeLogicalPlan>(
|
|
||||||
std::move(output_plan), 0.0, AstStorage{}, symbol_table));
|
|
||||||
|
|
||||||
summary["planning_time"] = planning_timer.Elapsed().count();
|
|
||||||
|
|
||||||
return Results(&db_accessor, parameters, plan, output_symbols, header,
|
|
||||||
summary, parsed_query.required_privileges,
|
|
||||||
/* is_profile_query */ false,
|
|
||||||
/* should_abort_query */ false);
|
|
||||||
#else
|
|
||||||
throw utils::NotYetImplemented("Dump database");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Callback callback;
|
Callback callback;
|
||||||
if (auto *index_query = utils::Downcast<IndexQuery>(parsed_query.query)) {
|
if (auto *index_query = utils::Downcast<IndexQuery>(parsed_query.query)) {
|
||||||
if (in_explicit_transaction) {
|
if (in_explicit_transaction) {
|
||||||
@@ -1035,30 +451,6 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
};
|
};
|
||||||
callback =
|
callback =
|
||||||
HandleIndexQuery(index_query, invalidate_plan_cache, &db_accessor);
|
HandleIndexQuery(index_query, invalidate_plan_cache, &db_accessor);
|
||||||
} else if (auto *auth_query =
|
|
||||||
utils::Downcast<AuthQuery>(parsed_query.query)) {
|
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
throw utils::NotYetImplemented(
|
|
||||||
"Managing user privileges is not yet supported in Memgraph HA "
|
|
||||||
"instance.");
|
|
||||||
#else
|
|
||||||
if (in_explicit_transaction) {
|
|
||||||
throw UserModificationInMulticommandTxException();
|
|
||||||
}
|
|
||||||
callback = HandleAuthQuery(auth_query, auth_, parameters, &db_accessor);
|
|
||||||
#endif
|
|
||||||
} else if (auto *stream_query =
|
|
||||||
utils::Downcast<StreamQuery>(parsed_query.query)) {
|
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
throw utils::NotYetImplemented(
|
|
||||||
"Graph streams are not yet supported in Memgraph HA instance.");
|
|
||||||
#else
|
|
||||||
if (in_explicit_transaction) {
|
|
||||||
throw StreamClauseInMulticommandTxException();
|
|
||||||
}
|
|
||||||
callback = HandleStreamQuery(stream_query, kafka_streams_, parameters,
|
|
||||||
&db_accessor);
|
|
||||||
#endif
|
|
||||||
} else if (auto *info_query =
|
} else if (auto *info_query =
|
||||||
utils::Downcast<InfoQuery>(parsed_query.query)) {
|
utils::Downcast<InfoQuery>(parsed_query.query)) {
|
||||||
callback = HandleInfoQuery(info_query, &db_accessor);
|
callback = HandleInfoQuery(info_query, &db_accessor);
|
||||||
@@ -1085,8 +477,9 @@ Interpreter::Results Interpreter::operator()(
|
|||||||
summary["planning_time"] = planning_time.count();
|
summary["planning_time"] = planning_time.count();
|
||||||
summary["cost_estimate"] = 0.0;
|
summary["cost_estimate"] = 0.0;
|
||||||
|
|
||||||
return Results(&db_accessor, parameters, plan, output_symbols,
|
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
|
||||||
callback.header, summary, parsed_query.required_privileges,
|
callback.header, std::move(summary),
|
||||||
|
execution_memory,
|
||||||
/* is_profile_query */ false, callback.should_abort_query);
|
/* is_profile_query */ false, callback.should_abort_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,8 +516,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
|
|||||||
// Convert antlr4 AST into Memgraph AST.
|
// Convert antlr4 AST into Memgraph AST.
|
||||||
frontend::CypherMainVisitor visitor(context, ast_storage);
|
frontend::CypherMainVisitor visitor(context, ast_storage);
|
||||||
visitor.visit(parser->tree());
|
visitor.visit(parser->tree());
|
||||||
return ParsedQuery{visitor.query(),
|
return ParsedQuery{visitor.query()};
|
||||||
query::GetRequiredPrivileges(visitor.query())};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stripped_query_hash = fnv(stripped_query);
|
auto stripped_query_hash = fnv(stripped_query);
|
||||||
@@ -1153,8 +545,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
|
|||||||
AstStorage cached_ast_storage;
|
AstStorage cached_ast_storage;
|
||||||
frontend::CypherMainVisitor visitor(context, &cached_ast_storage);
|
frontend::CypherMainVisitor visitor(context, &cached_ast_storage);
|
||||||
visitor.visit(parser->tree());
|
visitor.visit(parser->tree());
|
||||||
CachedQuery cached_query{std::move(cached_ast_storage), visitor.query(),
|
CachedQuery cached_query{std::move(cached_ast_storage), visitor.query()};
|
||||||
query::GetRequiredPrivileges(visitor.query())};
|
|
||||||
// Cache it.
|
// Cache it.
|
||||||
ast_it =
|
ast_it =
|
||||||
ast_cache_accessor.insert(stripped_query_hash, std::move(cached_query))
|
ast_cache_accessor.insert(stripped_query_hash, std::move(cached_query))
|
||||||
@@ -1163,8 +554,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
|
|||||||
ast_storage->properties_ = ast_it->second.ast_storage.properties_;
|
ast_storage->properties_ = ast_it->second.ast_storage.properties_;
|
||||||
ast_storage->labels_ = ast_it->second.ast_storage.labels_;
|
ast_storage->labels_ = ast_it->second.ast_storage.labels_;
|
||||||
ast_storage->edge_types_ = ast_it->second.ast_storage.edge_types_;
|
ast_storage->edge_types_ = ast_it->second.ast_storage.edge_types_;
|
||||||
return ParsedQuery{ast_it->second.query->Clone(ast_storage),
|
return ParsedQuery{ast_it->second.query->Clone(ast_storage)};
|
||||||
ast_it->second.required_privileges};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<frontend::StrippedQuery, Interpreter::ParsedQuery>
|
std::pair<frontend::StrippedQuery, Interpreter::ParsedQuery>
|
||||||
|
|||||||
@@ -17,14 +17,6 @@
|
|||||||
DECLARE_bool(query_cost_planner);
|
DECLARE_bool(query_cost_planner);
|
||||||
DECLARE_int32(query_plan_cache_ttl);
|
DECLARE_int32(query_plan_cache_ttl);
|
||||||
|
|
||||||
namespace auth {
|
|
||||||
class Auth;
|
|
||||||
} // namespace auth
|
|
||||||
|
|
||||||
namespace integrations::kafka {
|
|
||||||
class Streams;
|
|
||||||
} // namespace integrations::kafka
|
|
||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
// TODO: Maybe this should move to query/plan/planner.
|
// TODO: Maybe this should move to query/plan/planner.
|
||||||
@@ -63,19 +55,16 @@ class Interpreter {
|
|||||||
struct CachedQuery {
|
struct CachedQuery {
|
||||||
AstStorage ast_storage;
|
AstStorage ast_storage;
|
||||||
Query *query;
|
Query *query;
|
||||||
std::vector<AuthQuery::Privilege> required_privileges;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using PlanCacheT = ConcurrentMap<HashType, std::shared_ptr<CachedPlan>>;
|
using PlanCacheT = ConcurrentMap<HashType, std::shared_ptr<CachedPlan>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Wraps a `Query` that was created as a result of parsing a query string
|
* Wraps a `Query` that was created as a result of parsing a query string.
|
||||||
* along with its privileges.
|
|
||||||
*/
|
*/
|
||||||
struct ParsedQuery {
|
struct ParsedQuery {
|
||||||
Query *query;
|
Query *query;
|
||||||
std::vector<AuthQuery::Privilege> required_privileges;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,18 +78,15 @@ class Interpreter {
|
|||||||
std::shared_ptr<CachedPlan> plan,
|
std::shared_ptr<CachedPlan> plan,
|
||||||
std::vector<Symbol> output_symbols, std::vector<std::string> header,
|
std::vector<Symbol> output_symbols, std::vector<std::string> header,
|
||||||
std::map<std::string, TypedValue> summary,
|
std::map<std::string, TypedValue> summary,
|
||||||
std::vector<AuthQuery::Privilege> privileges,
|
utils::MemoryResource *execution_memory,
|
||||||
bool is_profile_query = false, bool should_abort_query = false)
|
bool is_profile_query = false, bool should_abort_query = false)
|
||||||
: ctx_{db_accessor},
|
: ctx_{db_accessor},
|
||||||
plan_(plan),
|
plan_(plan),
|
||||||
execution_memory_(std::make_unique<utils::MonotonicBufferResource>(
|
cursor_(plan_->plan().MakeCursor(execution_memory)),
|
||||||
kExecutionMemoryBlockSize)),
|
frame_(plan_->symbol_table().max_position(), execution_memory),
|
||||||
cursor_(plan_->plan().MakeCursor(execution_memory_.get())),
|
output_symbols_(std::move(output_symbols)),
|
||||||
frame_(plan_->symbol_table().max_position(), execution_memory_.get()),
|
header_(std::move(header)),
|
||||||
output_symbols_(output_symbols),
|
summary_(std::move(summary)),
|
||||||
header_(header),
|
|
||||||
summary_(summary),
|
|
||||||
privileges_(std::move(privileges)),
|
|
||||||
should_abort_query_(should_abort_query) {
|
should_abort_query_(should_abort_query) {
|
||||||
ctx_.is_profile_query = is_profile_query;
|
ctx_.is_profile_query = is_profile_query;
|
||||||
ctx_.symbol_table = plan_->symbol_table();
|
ctx_.symbol_table = plan_->symbol_table();
|
||||||
@@ -139,8 +125,10 @@ class Interpreter {
|
|||||||
// enough for a single Pull.
|
// enough for a single Pull.
|
||||||
constexpr size_t stack_size = 256 * 1024;
|
constexpr size_t stack_size = 256 * 1024;
|
||||||
char stack_data[stack_size];
|
char stack_data[stack_size];
|
||||||
utils::MonotonicBufferResource memory(&stack_data[0], stack_size);
|
utils::MonotonicBufferResource monotonic_memory(&stack_data[0], stack_size);
|
||||||
ctx_.evaluation_context.memory = &memory;
|
// TODO (mferencevic): Tune the parameters accordingly.
|
||||||
|
utils::PoolResource pool_memory(128, 1024, &monotonic_memory);
|
||||||
|
ctx_.evaluation_context.memory = &pool_memory;
|
||||||
// We can now Pull a result.
|
// We can now Pull a result.
|
||||||
bool return_value = cursor_->Pull(frame_, ctx_);
|
bool return_value = cursor_->Pull(frame_, ctx_);
|
||||||
if (return_value && !output_symbols_.empty()) {
|
if (return_value && !output_symbols_.empty()) {
|
||||||
@@ -175,11 +163,13 @@ class Interpreter {
|
|||||||
while (Pull(stream)) continue;
|
while (Pull(stream)) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> &header() { return header_; }
|
const std::vector<std::string> &header() const & { return header_; }
|
||||||
const std::map<std::string, TypedValue> &summary() { return summary_; }
|
std::vector<std::string> &&header() && { return std::move(header_); }
|
||||||
|
const std::map<std::string, TypedValue> &summary() const & {
|
||||||
const std::vector<AuthQuery::Privilege> &privileges() {
|
return summary_;
|
||||||
return privileges_;
|
}
|
||||||
|
std::map<std::string, TypedValue> &&summary() && {
|
||||||
|
return std::move(summary_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldAbortQuery() const { return should_abort_query_; }
|
bool ShouldAbortQuery() const { return should_abort_query_; }
|
||||||
@@ -187,9 +177,6 @@ class Interpreter {
|
|||||||
private:
|
private:
|
||||||
ExecutionContext ctx_;
|
ExecutionContext ctx_;
|
||||||
std::shared_ptr<CachedPlan> plan_;
|
std::shared_ptr<CachedPlan> plan_;
|
||||||
// execution_memory_ is unique_ptr, because we are passing the address to
|
|
||||||
// cursor_, and we want to preserve the pointer in case we get moved.
|
|
||||||
std::unique_ptr<utils::MonotonicBufferResource> execution_memory_;
|
|
||||||
query::plan::UniqueCursorPtr cursor_;
|
query::plan::UniqueCursorPtr cursor_;
|
||||||
Frame frame_;
|
Frame frame_;
|
||||||
std::vector<Symbol> output_symbols_;
|
std::vector<Symbol> output_symbols_;
|
||||||
@@ -199,8 +186,6 @@ class Interpreter {
|
|||||||
|
|
||||||
double execution_time_{0};
|
double execution_time_{0};
|
||||||
|
|
||||||
std::vector<AuthQuery::Privilege> privileges_;
|
|
||||||
|
|
||||||
bool should_abort_query_;
|
bool should_abort_query_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,10 +204,8 @@ class Interpreter {
|
|||||||
virtual Results operator()(const std::string &query,
|
virtual Results operator()(const std::string &query,
|
||||||
database::GraphDbAccessor &db_accessor,
|
database::GraphDbAccessor &db_accessor,
|
||||||
const std::map<std::string, PropertyValue> ¶ms,
|
const std::map<std::string, PropertyValue> ¶ms,
|
||||||
bool in_explicit_transaction);
|
bool in_explicit_transaction,
|
||||||
|
utils::MemoryResource *execution_memory);
|
||||||
auth::Auth *auth_ = nullptr;
|
|
||||||
integrations::kafka::Streams *kafka_streams_ = nullptr;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::pair<frontend::StrippedQuery, ParsedQuery> StripAndParseQuery(
|
std::pair<frontend::StrippedQuery, ParsedQuery> StripAndParseQuery(
|
||||||
|
|||||||
@@ -360,7 +360,9 @@ json ToJson(const EdgeCreationInfo &edge_info,
|
|||||||
|
|
||||||
json ToJson(const Aggregate::Element &elem) {
|
json ToJson(const Aggregate::Element &elem) {
|
||||||
json json;
|
json json;
|
||||||
json["value"] = ToJson(elem.value);
|
if (elem.value) {
|
||||||
|
json["value"] = ToJson(elem.value);
|
||||||
|
}
|
||||||
if (elem.key) {
|
if (elem.key) {
|
||||||
json["key"] = ToJson(elem.key);
|
json["key"] = ToJson(elem.key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
|||||||
|
|
||||||
#define VISIT_BINARY_OPERATOR(BinaryOperator) \
|
#define VISIT_BINARY_OPERATOR(BinaryOperator) \
|
||||||
bool PostVisit(BinaryOperator &op) override { \
|
bool PostVisit(BinaryOperator &op) override { \
|
||||||
CHECK(has_aggregation_.size() >= 2U) \
|
CHECK(has_aggregation_.size() >= 2U) \
|
||||||
<< "Expected at least 2 has_aggregation_ flags."; \
|
<< "Expected at least 2 has_aggregation_ flags."; \
|
||||||
/* has_aggregation_ stack is reversed, last result is from the 2nd */ \
|
/* has_aggregation_ stack is reversed, last result is from the 2nd */ \
|
||||||
/* expression. */ \
|
/* expression. */ \
|
||||||
@@ -350,6 +350,15 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PostVisit(RegexMatch ®ex_match) override {
|
||||||
|
CHECK(has_aggregation_.size() >= 2U)
|
||||||
|
<< "Expected 2 has_aggregation_ flags for RegexMatch arguments";
|
||||||
|
bool has_aggr = has_aggregation_.back();
|
||||||
|
has_aggregation_.pop_back();
|
||||||
|
has_aggregation_.back() |= has_aggr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Creates NamedExpression with an Identifier for each user declared symbol.
|
// Creates NamedExpression with an Identifier for each user declared symbol.
|
||||||
// This should be used when body.all_identifiers is true, to generate
|
// This should be used when body.all_identifiers is true, to generate
|
||||||
// expressions for Produce operator.
|
// expressions for Produce operator.
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "query/context.hpp"
|
||||||
#include "query/plan/profile.hpp"
|
#include "query/plan/profile.hpp"
|
||||||
|
#include "utils/likely.hpp"
|
||||||
#include "utils/tsc.hpp"
|
#include "utils/tsc.hpp"
|
||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|||||||
@@ -63,20 +63,13 @@ void query::Repl(database::GraphDb *db, query::Interpreter *interpreter) {
|
|||||||
try {
|
try {
|
||||||
auto dba = db->Access();
|
auto dba = db->Access();
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
#ifndef MG_DISTRIBUTED
|
auto results =
|
||||||
auto results = (*interpreter)(command, dba, {}, false);
|
(*interpreter)(command, dba, {}, false, utils::NewDeleteResource());
|
||||||
#else
|
|
||||||
auto results = (*interpreter)(command, *dba, {}, false);
|
|
||||||
#endif
|
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
std::cout << stream;
|
std::cout << stream;
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
dba.Commit();
|
dba.Commit();
|
||||||
#else
|
|
||||||
dba->Commit();
|
|
||||||
#endif
|
|
||||||
} catch (const query::SyntaxException &e) {
|
} catch (const query::SyntaxException &e) {
|
||||||
std::cout << "SYNTAX EXCEPTION: " << e.what() << std::endl;
|
std::cout << "SYNTAX EXCEPTION: " << e.what() << std::endl;
|
||||||
} catch (const query::LexingException &e) {
|
} catch (const query::LexingException &e) {
|
||||||
|
|||||||
@@ -5,22 +5,29 @@
|
|||||||
#include "query/exceptions.hpp"
|
#include "query/exceptions.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
#include "utils/likely.hpp"
|
#include "utils/likely.hpp"
|
||||||
|
#include "utils/memory.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
|
static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
|
||||||
|
|
||||||
class TransactionEngine final {
|
class TransactionEngine final {
|
||||||
public:
|
public:
|
||||||
TransactionEngine(database::GraphDb *db, Interpreter *interpreter)
|
TransactionEngine(database::GraphDb *db, Interpreter *interpreter)
|
||||||
: db_(db), interpreter_(interpreter) {}
|
: db_(db),
|
||||||
|
interpreter_(interpreter),
|
||||||
|
execution_memory_(&initial_memory_block_[0],
|
||||||
|
kExecutionMemoryBlockSize) {}
|
||||||
|
|
||||||
~TransactionEngine() { Abort(); }
|
~TransactionEngine() { Abort(); }
|
||||||
|
|
||||||
std::pair<std::vector<std::string>, std::vector<query::AuthQuery::Privilege>>
|
std::vector<std::string>
|
||||||
Interpret(const std::string &query,
|
Interpret(const std::string &query,
|
||||||
const std::map<std::string, PropertyValue> ¶ms) {
|
const std::map<std::string, PropertyValue> ¶ms) {
|
||||||
// Clear pending results.
|
// Clear pending results.
|
||||||
results_ = std::nullopt;
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
|
|
||||||
// Check the query for transaction commands.
|
// Check the query for transaction commands.
|
||||||
auto query_upper = utils::Trim(utils::ToUpperCase(query));
|
auto query_upper = utils::Trim(utils::ToUpperCase(query));
|
||||||
@@ -65,17 +72,18 @@ class TransactionEngine final {
|
|||||||
if (in_explicit_transaction_ && db_accessor_) AdvanceCommand();
|
if (in_explicit_transaction_ && db_accessor_) AdvanceCommand();
|
||||||
|
|
||||||
// Create a DB accessor if we don't yet have one.
|
// Create a DB accessor if we don't yet have one.
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
if (!db_accessor_) db_accessor_.emplace(db_->Access());
|
if (!db_accessor_) db_accessor_.emplace(db_->Access());
|
||||||
#else
|
|
||||||
if (!db_accessor_) db_accessor_ = db_->Access();
|
// Clear leftover results.
|
||||||
#endif
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
|
|
||||||
// Interpret the query and return the headers.
|
// Interpret the query and return the headers.
|
||||||
try {
|
try {
|
||||||
results_.emplace((*interpreter_)(query, *db_accessor_, params,
|
results_.emplace((*interpreter_)(query, *db_accessor_, params,
|
||||||
in_explicit_transaction_));
|
in_explicit_transaction_,
|
||||||
return {results_->header(), results_->privileges()};
|
&execution_memory_));
|
||||||
|
return std::move(results_->header());
|
||||||
} catch (const utils::BasicException &) {
|
} catch (const utils::BasicException &) {
|
||||||
AbortCommand();
|
AbortCommand();
|
||||||
throw;
|
throw;
|
||||||
@@ -103,11 +111,6 @@ class TransactionEngine final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return summary;
|
return summary;
|
||||||
#ifdef MG_SINGLE_NODE_HA
|
|
||||||
} catch (const query::HintedAbortError &) {
|
|
||||||
AbortCommand();
|
|
||||||
throw utils::BasicException("Transaction was asked to abort.");
|
|
||||||
#endif
|
|
||||||
} catch (const utils::BasicException &) {
|
} catch (const utils::BasicException &) {
|
||||||
AbortCommand();
|
AbortCommand();
|
||||||
throw;
|
throw;
|
||||||
@@ -116,25 +119,18 @@ class TransactionEngine final {
|
|||||||
|
|
||||||
void Abort() {
|
void Abort() {
|
||||||
results_ = std::nullopt;
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
expect_rollback_ = false;
|
expect_rollback_ = false;
|
||||||
in_explicit_transaction_ = false;
|
in_explicit_transaction_ = false;
|
||||||
if (!db_accessor_) return;
|
if (!db_accessor_) return;
|
||||||
db_accessor_->Abort();
|
db_accessor_->Abort();
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
db_accessor_ = std::nullopt;
|
db_accessor_ = std::nullopt;
|
||||||
#else
|
|
||||||
db_accessor_ = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
database::GraphDb *db_{nullptr};
|
database::GraphDb *db_{nullptr};
|
||||||
Interpreter *interpreter_{nullptr};
|
Interpreter *interpreter_{nullptr};
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
std::optional<database::GraphDbAccessor> db_accessor_;
|
std::optional<database::GraphDbAccessor> db_accessor_;
|
||||||
#else
|
|
||||||
std::unique_ptr<database::GraphDbAccessor> db_accessor_;
|
|
||||||
#endif
|
|
||||||
// The `query::Interpreter::Results` object MUST be destroyed before the
|
// The `query::Interpreter::Results` object MUST be destroyed before the
|
||||||
// `database::GraphDbAccessor` is destroyed because the `Results` object holds
|
// `database::GraphDbAccessor` is destroyed because the `Results` object holds
|
||||||
// references to the `GraphDb` object and will crash the database when
|
// references to the `GraphDb` object and will crash the database when
|
||||||
@@ -143,25 +139,27 @@ class TransactionEngine final {
|
|||||||
bool in_explicit_transaction_{false};
|
bool in_explicit_transaction_{false};
|
||||||
bool expect_rollback_{false};
|
bool expect_rollback_{false};
|
||||||
|
|
||||||
|
uint8_t initial_memory_block_[kExecutionMemoryBlockSize];
|
||||||
|
utils::MonotonicBufferResource execution_memory_;
|
||||||
|
|
||||||
void Commit() {
|
void Commit() {
|
||||||
results_ = std::nullopt;
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
if (!db_accessor_) return;
|
if (!db_accessor_) return;
|
||||||
db_accessor_->Commit();
|
db_accessor_->Commit();
|
||||||
#ifndef MG_DISTRIBUTED
|
|
||||||
db_accessor_ = std::nullopt;
|
db_accessor_ = std::nullopt;
|
||||||
#else
|
|
||||||
db_accessor_ = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvanceCommand() {
|
void AdvanceCommand() {
|
||||||
results_ = std::nullopt;
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
if (!db_accessor_) return;
|
if (!db_accessor_) return;
|
||||||
db_accessor_->AdvanceCommand();
|
db_accessor_->AdvanceCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbortCommand() {
|
void AbortCommand() {
|
||||||
results_ = std::nullopt;
|
results_ = std::nullopt;
|
||||||
|
execution_memory_.Release();
|
||||||
if (in_explicit_transaction_) {
|
if (in_explicit_transaction_) {
|
||||||
expect_rollback_ = true;
|
expect_rollback_ = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class Transaction;
|
|||||||
|
|
||||||
class RecordLock {
|
class RecordLock {
|
||||||
public:
|
public:
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
LockStatus Lock(const tx::Transaction &id, tx::Engine &engine);
|
LockStatus Lock(const tx::Transaction &id, tx::Engine &engine);
|
||||||
|
|
||||||
void Unlock();
|
void Unlock();
|
||||||
|
|||||||
@@ -7,6 +7,16 @@
|
|||||||
|
|
||||||
#include "utils/exceptions.hpp"
|
#include "utils/exceptions.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception raised by the PropertyValue system. Typically when
|
||||||
|
* trying to perform operations (such as addition) on PropertyValues
|
||||||
|
* of incompatible Types.
|
||||||
|
*/
|
||||||
|
class PropertyValueException : public utils::StacktraceException {
|
||||||
|
public:
|
||||||
|
using utils::StacktraceException::StacktraceException;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulation of a value and its type in a class that has no compile-time
|
* Encapsulation of a value and its type in a class that has no compile-time
|
||||||
* info about the type.
|
* info about the type.
|
||||||
@@ -92,6 +102,69 @@ class PropertyValue {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T &Value();
|
T &Value();
|
||||||
|
|
||||||
|
bool ValueBool() const {
|
||||||
|
if (type_ != Type::Bool) {
|
||||||
|
throw PropertyValueException("This value isn't a bool!");
|
||||||
|
}
|
||||||
|
return bool_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ValueInt() const {
|
||||||
|
if (type_ != Type::Int) {
|
||||||
|
throw PropertyValueException("This value isn't a int!");
|
||||||
|
}
|
||||||
|
return int_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ValueDouble() const {
|
||||||
|
if (type_ != Type::Double) {
|
||||||
|
throw PropertyValueException("This value isn't a double!");
|
||||||
|
}
|
||||||
|
return double_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &ValueString() const {
|
||||||
|
if (type_ != Type::String) {
|
||||||
|
throw PropertyValueException("The value isn't a string!");
|
||||||
|
}
|
||||||
|
return string_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<PropertyValue> &ValueList() const {
|
||||||
|
if (type_ != Type::List) {
|
||||||
|
throw PropertyValueException("The value isn't a list!");
|
||||||
|
}
|
||||||
|
return list_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<std::string, PropertyValue> &ValueMap() const {
|
||||||
|
if (type_ != Type::Map) {
|
||||||
|
throw PropertyValueException("The value isn't a map!");
|
||||||
|
}
|
||||||
|
return map_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string &ValueString() {
|
||||||
|
if (type_ != Type::String) {
|
||||||
|
throw PropertyValueException("The value isn't a string!");
|
||||||
|
}
|
||||||
|
return string_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PropertyValue> &ValueList() {
|
||||||
|
if (type_ != Type::List) {
|
||||||
|
throw PropertyValueException("The value isn't a list!");
|
||||||
|
}
|
||||||
|
return list_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, PropertyValue> &ValueMap() {
|
||||||
|
if (type_ != Type::Map) {
|
||||||
|
throw PropertyValueException("The value isn't a map!");
|
||||||
|
}
|
||||||
|
return map_v;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DestroyValue();
|
void DestroyValue();
|
||||||
|
|
||||||
@@ -113,16 +186,6 @@ class PropertyValue {
|
|||||||
Type type_;
|
Type type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception raised by the PropertyValue system. Typically when
|
|
||||||
* trying to perform operations (such as addition) on PropertyValues
|
|
||||||
* of incompatible Types.
|
|
||||||
*/
|
|
||||||
class PropertyValueException : public utils::StacktraceException {
|
|
||||||
public:
|
|
||||||
using utils::StacktraceException::StacktraceException;
|
|
||||||
};
|
|
||||||
|
|
||||||
// stream output
|
// stream output
|
||||||
std::ostream &operator<<(std::ostream &os, const PropertyValue::Type type);
|
std::ostream &operator<<(std::ostream &os, const PropertyValue::Type type);
|
||||||
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);
|
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ class VersionList {
|
|||||||
* older visible record when this update is called.
|
* older visible record when this update is called.
|
||||||
*
|
*
|
||||||
* @param t The transaction
|
* @param t The transaction
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
T *update(const tx::Transaction &t) {
|
T *update(const tx::Transaction &t) {
|
||||||
DCHECK(head_ != nullptr) << "Head is nullptr on update.";
|
DCHECK(head_ != nullptr) << "Head is nullptr on update.";
|
||||||
@@ -208,7 +210,11 @@ class VersionList {
|
|||||||
return update(old_record, t);
|
return update(old_record, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Makes the given record as being expired by the given transaction. */
|
/**
|
||||||
|
* Makes the given record as being expired by the given transaction.
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void remove(T *record, const tx::Transaction &t) {
|
void remove(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on removal.";
|
DCHECK(record != nullptr) << "Record is nullptr on removal.";
|
||||||
lock_and_validate(record, t);
|
lock_and_validate(record, t);
|
||||||
@@ -220,6 +226,8 @@ class VersionList {
|
|||||||
int64_t cypher_id() { return utils::MemcpyCast<int64_t>(gid_); }
|
int64_t cypher_id() { return utils::MemcpyCast<int64_t>(gid_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
|
/// @throw SerializationError
|
||||||
void lock_and_validate(T *record, const tx::Transaction &t) {
|
void lock_and_validate(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on lock and validation.";
|
DCHECK(record != nullptr) << "Record is nullptr on lock and validation.";
|
||||||
|
|
||||||
@@ -234,6 +242,8 @@ class VersionList {
|
|||||||
throw SerializationError();
|
throw SerializationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
|
/// @throw SerializationError
|
||||||
T *update(T *record, const tx::Transaction &t) {
|
T *update(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on update.";
|
DCHECK(record != nullptr) << "Record is nullptr on update.";
|
||||||
lock_and_validate(record, t);
|
lock_and_validate(record, t);
|
||||||
|
|||||||
@@ -54,13 +54,27 @@ class RecordAccessor {
|
|||||||
/** Gets the property for the given key. */
|
/** Gets the property for the given key. */
|
||||||
PropertyValue PropsAt(storage::Property key) const;
|
PropertyValue PropsAt(storage::Property key) const;
|
||||||
|
|
||||||
/** Sets a value on the record for the given property. */
|
/**
|
||||||
|
* Sets a value on the record for the given property.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
* @throw ConstraintViolationException
|
||||||
|
*/
|
||||||
void PropsSet(storage::Property key, PropertyValue value);
|
void PropsSet(storage::Property key, PropertyValue value);
|
||||||
|
|
||||||
/** Erases the property for the given key. */
|
/**
|
||||||
|
* Erases the property for the given key.
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void PropsErase(storage::Property key);
|
void PropsErase(storage::Property key);
|
||||||
|
|
||||||
/** Removes all the properties from this record. */
|
/**
|
||||||
|
* Removes all the properties from this record.
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void PropsClear();
|
void PropsClear();
|
||||||
|
|
||||||
/** Returns the properties of this record. */
|
/** Returns the properties of this record. */
|
||||||
@@ -127,7 +141,9 @@ class RecordAccessor {
|
|||||||
* It is not legal to call this function on a Vertex/Edge that has been
|
* It is not legal to call this function on a Vertex/Edge that has been
|
||||||
* deleted in the current transaction+command.
|
* deleted in the current transaction+command.
|
||||||
*
|
*
|
||||||
* @throws RecordDeletedError
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
*/
|
*/
|
||||||
TRecord &update() const;
|
TRecord &update() const;
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,22 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
/** Returns the number of incoming edges. */
|
/** Returns the number of incoming edges. */
|
||||||
size_t in_degree() const;
|
size_t in_degree() const;
|
||||||
|
|
||||||
/** Adds a label to the Vertex. If the Vertex already has that label the call
|
/**
|
||||||
* has no effect. */
|
* Add a label to the Vertex.
|
||||||
|
* If the Vertex already has that label the call has no effect.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
* @throw ConstraintViolationException
|
||||||
|
*/
|
||||||
void add_label(storage::Label label);
|
void add_label(storage::Label label);
|
||||||
|
|
||||||
/** Removes a label from the Vertex. */
|
/**
|
||||||
|
* Removes a label from the Vertex.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void remove_label(storage::Label label);
|
void remove_label(storage::Label label);
|
||||||
|
|
||||||
/** Indicates if the Vertex has the given label. */
|
/** Indicates if the Vertex has the given label. */
|
||||||
@@ -138,12 +149,20 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
|
|||||||
|
|
||||||
/** Removes the given edge from the outgoing edges of this vertex. Note that
|
/** Removes the given edge from the outgoing edges of this vertex. Note that
|
||||||
* this operation should always be accompanied by the removal of the edge from
|
* this operation should always be accompanied by the removal of the edge from
|
||||||
* the incoming edges on the other side and edge deletion. */
|
* the incoming edges on the other side and edge deletion.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void RemoveOutEdge(mvcc::VersionList<Edge> *edge);
|
void RemoveOutEdge(mvcc::VersionList<Edge> *edge);
|
||||||
|
|
||||||
/** Removes the given edge from the incoming edges of this vertex. Note that
|
/** Removes the given edge from the incoming edges of this vertex. Note that
|
||||||
* this operation should always be accompanied by the removal of the edge from
|
* this operation should always be accompanied by the removal of the edge from
|
||||||
* the outgoing edges on the other side and edge deletion. */
|
* the outgoing edges on the other side and edge deletion.
|
||||||
|
* @throw RecordDeletedError
|
||||||
|
* @throw utils::LockTimeoutException
|
||||||
|
* @throw SerializationError
|
||||||
|
*/
|
||||||
void RemoveInEdge(mvcc::VersionList<Edge> *edge);
|
void RemoveInEdge(mvcc::VersionList<Edge> *edge);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ const nlohmann::json GetSystemInfo() {
|
|||||||
auto mem_data = utils::ReadLines("/proc/meminfo");
|
auto mem_data = utils::ReadLines("/proc/meminfo");
|
||||||
for (auto &row : mem_data) {
|
for (auto &row : mem_data) {
|
||||||
auto tmp = utils::Trim(row);
|
auto tmp = utils::Trim(row);
|
||||||
if (utils::StartsWith(row, "MemTotal")) {
|
if (utils::StartsWith(tmp, "MemTotal")) {
|
||||||
auto split = utils::Split(row);
|
auto split = utils::Split(tmp);
|
||||||
if (split.size() < 2) continue;
|
if (split.size() < 2) continue;
|
||||||
memory = std::stoull(split[1]);
|
memory = std::stoull(split[1]);
|
||||||
} else if (utils::StartsWith(row, "SwapTotal")) {
|
} else if (utils::StartsWith(tmp, "SwapTotal")) {
|
||||||
auto split = utils::Split(row);
|
auto split = utils::Split(tmp);
|
||||||
if (split.size() < 2) continue;
|
if (split.size() < 2) continue;
|
||||||
swap = std::stoull(split[1]);
|
swap = std::stoull(split[1]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class LockStore {
|
|||||||
public:
|
public:
|
||||||
LockHolder() = default;
|
LockHolder() = default;
|
||||||
|
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
LockHolder(RecordLock *lock, const Transaction &tx, tx::Engine &engine)
|
LockHolder(RecordLock *lock, const Transaction &tx, tx::Engine &engine)
|
||||||
: lock_(lock) {
|
: lock_(lock) {
|
||||||
DCHECK(lock != nullptr) << "Lock is nullptr.";
|
DCHECK(lock != nullptr) << "Lock is nullptr.";
|
||||||
@@ -57,6 +58,7 @@ class LockStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
void Take(RecordLock *lock, const tx::Transaction &tx, tx::Engine &engine) {
|
void Take(RecordLock *lock, const tx::Transaction &tx, tx::Engine &engine) {
|
||||||
// Creating a lock holder locks the version list to the given transaction.
|
// Creating a lock holder locks the version list to the given transaction.
|
||||||
// Note that it's an op that can take a long time (if there are multiple
|
// Note that it's an op that can take a long time (if there are multiple
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ class Engine final {
|
|||||||
Engine &operator=(const Engine &) = delete;
|
Engine &operator=(const Engine &) = delete;
|
||||||
Engine &operator=(Engine &&) = delete;
|
Engine &operator=(Engine &&) = delete;
|
||||||
|
|
||||||
|
/// @throw TransactionEngineError
|
||||||
Transaction *Begin();
|
Transaction *Begin();
|
||||||
/// Blocking transactions are used when we can't allow any other transaction to
|
/// Blocking transactions are used when we can't allow any other transaction to
|
||||||
/// run (besides this one). This is the reason why this transactions blocks the
|
/// run (besides this one). This is the reason why this transactions blocks the
|
||||||
/// engine from creating new transactions and waits for the existing ones to
|
/// engine from creating new transactions and waits for the existing ones to
|
||||||
/// finish.
|
/// finish.
|
||||||
|
/// @throw TransactionEngineError
|
||||||
Transaction *BeginBlocking(std::optional<TransactionId> parent_tx);
|
Transaction *BeginBlocking(std::optional<TransactionId> parent_tx);
|
||||||
|
/// @throw TransactionException
|
||||||
CommandId Advance(TransactionId id);
|
CommandId Advance(TransactionId id);
|
||||||
CommandId UpdateCommand(TransactionId id);
|
CommandId UpdateCommand(TransactionId id);
|
||||||
void Commit(const Transaction &t);
|
void Commit(const Transaction &t);
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class Transaction final {
|
|||||||
public:
|
public:
|
||||||
/// Acquires the lock over the given RecordLock, preventing other transactions
|
/// Acquires the lock over the given RecordLock, preventing other transactions
|
||||||
/// from doing the same
|
/// from doing the same
|
||||||
|
/// @throw utils::LockTimeoutException
|
||||||
void TakeLock(RecordLock &lock) const { locks_.Take(&lock, *this, engine_); }
|
void TakeLock(RecordLock &lock) const { locks_.Take(&lock, *this, engine_); }
|
||||||
|
|
||||||
/// Transaction's id. Unique in the engine that owns it
|
/// Transaction's id. Unique in the engine that owns it
|
||||||
@@ -81,7 +82,8 @@ class Transaction final {
|
|||||||
auto blocking() const { return blocking_; }
|
auto blocking() const { return blocking_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Function used to advance the command.
|
/// Function used to advance the command.
|
||||||
|
/// @throw TransactionError
|
||||||
CommandId AdvanceCommand() {
|
CommandId AdvanceCommand() {
|
||||||
if (cid_ == std::numeric_limits<CommandId>::max()) {
|
if (cid_ == std::numeric_limits<CommandId>::max()) {
|
||||||
throw TransactionError(
|
throw TransactionError(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ set(utils_src_files
|
|||||||
memory.cpp
|
memory.cpp
|
||||||
signals.cpp
|
signals.cpp
|
||||||
thread.cpp
|
thread.cpp
|
||||||
thread/sync.cpp
|
|
||||||
uuid.cpp
|
uuid.cpp
|
||||||
watchdog.cpp)
|
watchdog.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <byteswap.h>
|
|
||||||
|
|
||||||
namespace utils {
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline T Bswap(T value);
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline int16_t Bswap<int16_t>(int16_t value) {
|
|
||||||
return __bswap_16(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline uint16_t Bswap<uint16_t>(uint16_t value) {
|
|
||||||
return __bswap_16(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline int32_t Bswap<int32_t>(int32_t value) {
|
|
||||||
return __bswap_32(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline uint32_t Bswap<uint32_t>(uint32_t value) {
|
|
||||||
return __bswap_32(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline int64_t Bswap<int64_t>(int64_t value) {
|
|
||||||
return __bswap_64(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline uint64_t Bswap<uint64_t>(uint64_t value) {
|
|
||||||
return __bswap_64(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace utils
|
|
||||||
61
src/utils/endian.hpp
Normal file
61
src/utils/endian.hpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include "utils/cast.hpp"
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
inline uint16_t HostToLittleEndian(uint16_t value) { return htole16(value); }
|
||||||
|
inline uint32_t HostToLittleEndian(uint32_t value) { return htole32(value); }
|
||||||
|
inline uint64_t HostToLittleEndian(uint64_t value) { return htole64(value); }
|
||||||
|
inline int16_t HostToLittleEndian(int16_t value) {
|
||||||
|
return MemcpyCast<int16_t>(htole16(MemcpyCast<uint16_t>(value)));
|
||||||
|
}
|
||||||
|
inline int32_t HostToLittleEndian(int32_t value) {
|
||||||
|
return MemcpyCast<int32_t>(htole32(MemcpyCast<uint32_t>(value)));
|
||||||
|
}
|
||||||
|
inline int64_t HostToLittleEndian(int64_t value) {
|
||||||
|
return MemcpyCast<int64_t>(htole64(MemcpyCast<uint64_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t LittleEndianToHost(uint16_t value) { return le16toh(value); }
|
||||||
|
inline uint32_t LittleEndianToHost(uint32_t value) { return le32toh(value); }
|
||||||
|
inline uint64_t LittleEndianToHost(uint64_t value) { return le64toh(value); }
|
||||||
|
inline int16_t LittleEndianToHost(int16_t value) {
|
||||||
|
return MemcpyCast<int16_t>(le16toh(MemcpyCast<uint16_t>(value)));
|
||||||
|
}
|
||||||
|
inline int32_t LittleEndianToHost(int32_t value) {
|
||||||
|
return MemcpyCast<int32_t>(le32toh(MemcpyCast<uint32_t>(value)));
|
||||||
|
}
|
||||||
|
inline int64_t LittleEndianToHost(int64_t value) {
|
||||||
|
return MemcpyCast<int64_t>(le64toh(MemcpyCast<uint64_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t HostToBigEndian(uint16_t value) { return htobe16(value); }
|
||||||
|
inline uint32_t HostToBigEndian(uint32_t value) { return htobe32(value); }
|
||||||
|
inline uint64_t HostToBigEndian(uint64_t value) { return htobe64(value); }
|
||||||
|
inline int16_t HostToBigEndian(int16_t value) {
|
||||||
|
return MemcpyCast<int16_t>(htobe16(MemcpyCast<uint16_t>(value)));
|
||||||
|
}
|
||||||
|
inline int32_t HostToBigEndian(int32_t value) {
|
||||||
|
return MemcpyCast<int32_t>(htobe32(MemcpyCast<uint32_t>(value)));
|
||||||
|
}
|
||||||
|
inline int64_t HostToBigEndian(int64_t value) {
|
||||||
|
return MemcpyCast<int64_t>(htobe64(MemcpyCast<uint64_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t BigEndianToHost(uint16_t value) { return be16toh(value); }
|
||||||
|
inline uint32_t BigEndianToHost(uint32_t value) { return be32toh(value); }
|
||||||
|
inline uint64_t BigEndianToHost(uint64_t value) { return be64toh(value); }
|
||||||
|
inline int16_t BigEndianToHost(int16_t value) {
|
||||||
|
return MemcpyCast<int16_t>(be16toh(MemcpyCast<uint16_t>(value)));
|
||||||
|
}
|
||||||
|
inline int32_t BigEndianToHost(int32_t value) {
|
||||||
|
return MemcpyCast<int32_t>(be32toh(MemcpyCast<uint32_t>(value)));
|
||||||
|
}
|
||||||
|
inline int64_t BigEndianToHost(int64_t value) {
|
||||||
|
return MemcpyCast<int64_t>(be64toh(MemcpyCast<uint64_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
@@ -37,10 +37,7 @@ MonotonicBufferResource::MonotonicBufferResource(void *buffer,
|
|||||||
MemoryResource *memory)
|
MemoryResource *memory)
|
||||||
: memory_(memory),
|
: memory_(memory),
|
||||||
initial_buffer_(buffer),
|
initial_buffer_(buffer),
|
||||||
initial_size_(buffer_size),
|
initial_size_(buffer_size) {}
|
||||||
next_buffer_size_(GrowMonotonicBuffer(
|
|
||||||
initial_size_, std::numeric_limits<size_t>::max() - sizeof(Buffer))) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MonotonicBufferResource::MonotonicBufferResource(
|
MonotonicBufferResource::MonotonicBufferResource(
|
||||||
MonotonicBufferResource &&other) noexcept
|
MonotonicBufferResource &&other) noexcept
|
||||||
@@ -48,6 +45,7 @@ MonotonicBufferResource::MonotonicBufferResource(
|
|||||||
current_buffer_(other.current_buffer_),
|
current_buffer_(other.current_buffer_),
|
||||||
initial_buffer_(other.initial_buffer_),
|
initial_buffer_(other.initial_buffer_),
|
||||||
initial_size_(other.initial_size_),
|
initial_size_(other.initial_size_),
|
||||||
|
next_buffer_size_(other.next_buffer_size_),
|
||||||
allocated_(other.allocated_) {
|
allocated_(other.allocated_) {
|
||||||
other.current_buffer_ = nullptr;
|
other.current_buffer_ = nullptr;
|
||||||
}
|
}
|
||||||
@@ -60,6 +58,7 @@ MonotonicBufferResource &MonotonicBufferResource::operator=(
|
|||||||
current_buffer_ = other.current_buffer_;
|
current_buffer_ = other.current_buffer_;
|
||||||
initial_buffer_ = other.initial_buffer_;
|
initial_buffer_ = other.initial_buffer_;
|
||||||
initial_size_ = other.initial_size_;
|
initial_size_ = other.initial_size_;
|
||||||
|
next_buffer_size_ = other.next_buffer_size_;
|
||||||
allocated_ = other.allocated_;
|
allocated_ = other.allocated_;
|
||||||
other.current_buffer_ = nullptr;
|
other.current_buffer_ = nullptr;
|
||||||
other.allocated_ = 0U;
|
other.allocated_ = 0U;
|
||||||
@@ -69,14 +68,14 @@ MonotonicBufferResource &MonotonicBufferResource::operator=(
|
|||||||
void MonotonicBufferResource::Release() {
|
void MonotonicBufferResource::Release() {
|
||||||
for (auto *b = current_buffer_; b;) {
|
for (auto *b = current_buffer_; b;) {
|
||||||
auto *next = b->next;
|
auto *next = b->next;
|
||||||
auto *ptr = b->data();
|
|
||||||
auto alloc_size = b->size();
|
auto alloc_size = b->size();
|
||||||
auto alignment = b->alignment;
|
auto alignment = b->alignment;
|
||||||
b->~Buffer();
|
b->~Buffer();
|
||||||
memory_->Deallocate(ptr, alloc_size, alignment);
|
memory_->Deallocate(b, alloc_size, alignment);
|
||||||
b = next;
|
b = next;
|
||||||
}
|
}
|
||||||
current_buffer_ = nullptr;
|
current_buffer_ = nullptr;
|
||||||
|
next_buffer_size_ = initial_size_;
|
||||||
allocated_ = 0U;
|
allocated_ = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,22 +84,31 @@ void *MonotonicBufferResource::DoAllocate(size_t bytes, size_t alignment) {
|
|||||||
auto push_current_buffer = [this, bytes, alignment](size_t next_size) {
|
auto push_current_buffer = [this, bytes, alignment](size_t next_size) {
|
||||||
// Set size so that the bytes fit.
|
// Set size so that the bytes fit.
|
||||||
size_t size = next_size > bytes ? next_size : bytes;
|
size_t size = next_size > bytes ? next_size : bytes;
|
||||||
// Handle the case when we need to align `Buffer::data` to a greater
|
// Simplify alignment by always using values greater or equal to max_align
|
||||||
// `alignment`. We will simply always allocate Ceil2(required_size), so that
|
size_t alloc_align = std::max(alignment, alignof(std::max_align_t));
|
||||||
// we can use the end of the allocated bytes for Buffer instance.
|
// Setup the Buffer area before `Buffer::data` such that `Buffer::data` is
|
||||||
|
// correctly aligned. We do this by allocating an additional `multiple` of
|
||||||
|
// `alignment` of bytes. `multiple` is determined by the size of Buffer.
|
||||||
|
// This will ensure that both Buffer fits and is correctly aligned.
|
||||||
|
// `Buffer::data` is also correctly aligned as we use the pointer after this
|
||||||
|
// `multiple` of `alignment` bytes.
|
||||||
static_assert(IsPow2(alignof(Buffer)),
|
static_assert(IsPow2(alignof(Buffer)),
|
||||||
"Buffer should not be a packed struct in order to be placed "
|
"Buffer should not be a packed struct in order to be placed "
|
||||||
"at the end of an allocation request");
|
"at the start of an allocation request");
|
||||||
size_t bytes = sizeof(Buffer) + size;
|
size_t bytes_for_buffer = std::max(alloc_align, sizeof(Buffer));
|
||||||
if (bytes < size) throw BadAlloc("Allocation size overflow");
|
size_t multiple = bytes_for_buffer / alloc_align;
|
||||||
size_t alloc_size = Ceil2(bytes);
|
if (bytes_for_buffer % alloc_align != 0) ++multiple;
|
||||||
if (alloc_size < bytes) throw BadAlloc("Allocation size overflow");
|
bytes_for_buffer = multiple * alloc_align;
|
||||||
size_t alloc_align = std::max(alignment, alignof(std::max_align_t));
|
if (bytes_for_buffer < sizeof(Buffer) ||
|
||||||
|
bytes_for_buffer % alloc_align != 0) {
|
||||||
|
throw BadAlloc("Allocation size overflow");
|
||||||
|
}
|
||||||
|
size_t alloc_size = bytes_for_buffer + size;
|
||||||
|
if (alloc_size < size) throw BadAlloc("Allocation size overflow");
|
||||||
void *ptr = memory_->Allocate(alloc_size, alloc_align);
|
void *ptr = memory_->Allocate(alloc_size, alloc_align);
|
||||||
// Instantiate the Buffer at the end of the allocated block.
|
// Instantiate the Buffer at the start of the allocated block.
|
||||||
current_buffer_ =
|
current_buffer_ = new (ptr)
|
||||||
new (reinterpret_cast<char *>(ptr) + alloc_size - sizeof(Buffer))
|
Buffer{current_buffer_, alloc_size - bytes_for_buffer, alloc_align};
|
||||||
Buffer{current_buffer_, alloc_size - sizeof(Buffer), alloc_align};
|
|
||||||
allocated_ = 0;
|
allocated_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,10 +220,15 @@ void Pool::Deallocate(void *p) {
|
|||||||
ptr < reinterpret_cast<uintptr_t>(chunk.data + data_size);
|
ptr < reinterpret_cast<uintptr_t>(chunk.data + data_size);
|
||||||
};
|
};
|
||||||
auto deallocate_block_from_chunk = [this, p](Chunk *chunk) {
|
auto deallocate_block_from_chunk = [this, p](Chunk *chunk) {
|
||||||
|
// NOTE: This check is not enough to cover all double-free issues.
|
||||||
|
CHECK(chunk->blocks_available < blocks_per_chunk_)
|
||||||
|
<< "Deallocating more blocks than a chunk can contain, possibly a "
|
||||||
|
"double-free situation or we have a bug in the allocator.";
|
||||||
// Link the block into the free-list
|
// Link the block into the free-list
|
||||||
auto *block = reinterpret_cast<unsigned char *>(p);
|
auto *block = reinterpret_cast<unsigned char *>(p);
|
||||||
*block = chunk->first_available_block_ix;
|
*block = chunk->first_available_block_ix;
|
||||||
chunk->first_available_block_ix = (block - chunk->data) / block_size_;
|
chunk->first_available_block_ix = (block - chunk->data) / block_size_;
|
||||||
|
chunk->blocks_available++;
|
||||||
};
|
};
|
||||||
if (is_in_chunk(*last_dealloc_chunk_)) {
|
if (is_in_chunk(*last_dealloc_chunk_)) {
|
||||||
deallocate_block_from_chunk(last_dealloc_chunk_);
|
deallocate_block_from_chunk(last_dealloc_chunk_);
|
||||||
@@ -243,6 +256,8 @@ void Pool::Release() {
|
|||||||
GetUpstreamResource()->Deallocate(chunk.data, data_size, alignment);
|
GetUpstreamResource()->Deallocate(chunk.data, data_size, alignment);
|
||||||
}
|
}
|
||||||
chunks_.clear();
|
chunks_.clear();
|
||||||
|
last_alloc_chunk_ = nullptr;
|
||||||
|
last_dealloc_chunk_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
@@ -350,6 +365,8 @@ void PoolResource::Release() {
|
|||||||
GetUpstreamResource()->Deallocate(big_block.data, big_block.bytes,
|
GetUpstreamResource()->Deallocate(big_block.data, big_block.bytes,
|
||||||
big_block.alignment);
|
big_block.alignment);
|
||||||
unpooled_.clear();
|
unpooled_.clear();
|
||||||
|
last_alloc_pool_ = nullptr;
|
||||||
|
last_dealloc_pool_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PoolResource END
|
// PoolResource END
|
||||||
|
|||||||
@@ -15,7 +15,11 @@
|
|||||||
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
|
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
|
||||||
// implement it fully. It should be available in the next major release
|
// implement it fully. It should be available in the next major release
|
||||||
// version, i.e. gcc 9.x.
|
// version, i.e. gcc 9.x.
|
||||||
|
#if _GLIBCXX_RELEASE < 9
|
||||||
#include <experimental/memory_resource>
|
#include <experimental/memory_resource>
|
||||||
|
#else
|
||||||
|
#include <memory_resource>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils/math.hpp"
|
#include "utils/math.hpp"
|
||||||
#include "utils/spin_lock.hpp"
|
#include "utils/spin_lock.hpp"
|
||||||
@@ -264,9 +268,13 @@ using AVector = std::vector<T, Allocator<T>>;
|
|||||||
/// Wraps std::pmr::memory_resource for use with out MemoryResource
|
/// Wraps std::pmr::memory_resource for use with out MemoryResource
|
||||||
class StdMemoryResource final : public MemoryResource {
|
class StdMemoryResource final : public MemoryResource {
|
||||||
public:
|
public:
|
||||||
/// Implicitly convert std::pmr::memory_resource to StdMemoryResource
|
#if _GLIBCXX_RELEASE < 9
|
||||||
StdMemoryResource(std::experimental::pmr::memory_resource *memory)
|
StdMemoryResource(std::experimental::pmr::memory_resource *memory)
|
||||||
: memory_(memory) {}
|
: memory_(memory) {}
|
||||||
|
#else
|
||||||
|
/// Implicitly convert std::pmr::memory_resource to StdMemoryResource
|
||||||
|
StdMemoryResource(std::pmr::memory_resource *memory) : memory_(memory) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void *DoAllocate(size_t bytes, size_t alignment) override {
|
void *DoAllocate(size_t bytes, size_t alignment) override {
|
||||||
@@ -292,12 +300,20 @@ class StdMemoryResource final : public MemoryResource {
|
|||||||
return *memory_ == *other_std->memory_;
|
return *memory_ == *other_std->memory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _GLIBCXX_RELEASE < 9
|
||||||
std::experimental::pmr::memory_resource *memory_;
|
std::experimental::pmr::memory_resource *memory_;
|
||||||
|
#else
|
||||||
|
std::pmr::memory_resource *memory_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
inline MemoryResource *NewDeleteResource() noexcept {
|
inline MemoryResource *NewDeleteResource() noexcept {
|
||||||
|
#if _GLIBCXX_RELEASE < 9
|
||||||
static StdMemoryResource memory(
|
static StdMemoryResource memory(
|
||||||
std::experimental::pmr::new_delete_resource());
|
std::experimental::pmr::new_delete_resource());
|
||||||
|
#else
|
||||||
|
static StdMemoryResource memory(std::pmr::new_delete_resource());
|
||||||
|
#endif
|
||||||
return &memory;
|
return &memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,12 +328,8 @@ inline MemoryResource *NewDeleteResource() noexcept {
|
|||||||
/// buffer is exhausted, a new one is requested from the upstream memory
|
/// buffer is exhausted, a new one is requested from the upstream memory
|
||||||
/// resource.
|
/// resource.
|
||||||
///
|
///
|
||||||
/// Note that each buffer of memory is actually a block of `Ceil2(size +
|
/// Note that each buffer of memory is actually a larger block of at *least*
|
||||||
/// sizeof(Buffer))` due to bookkeeping `Buffer` object being appended at the end.
|
/// `(size + sizeof(Buffer))` bytes due to bookkeeping `Buffer` object.
|
||||||
/// This means that if you use an `initial_size` of 1024 bytes, you will
|
|
||||||
/// actually allocate `Ceil2(1024 + sizeof(Buffer))` which will be 2048 bytes.
|
|
||||||
/// Therefore you will have `2048 - sizeof(Buffer)` bytes available before a new
|
|
||||||
/// buffer will need to be allocated.
|
|
||||||
class MonotonicBufferResource final : public MemoryResource {
|
class MonotonicBufferResource final : public MemoryResource {
|
||||||
public:
|
public:
|
||||||
/// Construct the resource with the buffer size of at least `initial_size`.
|
/// Construct the resource with the buffer size of at least `initial_size`.
|
||||||
@@ -361,10 +373,23 @@ class MonotonicBufferResource final : public MemoryResource {
|
|||||||
Buffer *next;
|
Buffer *next;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
size_t alignment;
|
size_t alignment;
|
||||||
|
|
||||||
|
/// Get the size of the area reserved for `this`
|
||||||
|
size_t bytes_for_buffer() const {
|
||||||
|
size_t bytes = std::max(alignment, sizeof(*this));
|
||||||
|
if (bytes > alignment) {
|
||||||
|
size_t multiple = bytes / alignment;
|
||||||
|
if (bytes % alignment != 0) ++multiple;
|
||||||
|
bytes = multiple * alignment;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get total allocated size.
|
/// Get total allocated size.
|
||||||
size_t size() const { return sizeof(*this) + capacity; }
|
size_t size() const { return bytes_for_buffer() + capacity; }
|
||||||
/// Get the pointer to data which is before the Buffer instance itself.
|
|
||||||
char *data() { return reinterpret_cast<char *>(this) - capacity; }
|
/// Get the pointer to data which is after the Buffer instance itself.
|
||||||
|
char *data() { return reinterpret_cast<char *>(this) + bytes_for_buffer(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
MemoryResource *memory_{NewDeleteResource()};
|
MemoryResource *memory_{NewDeleteResource()};
|
||||||
|
|||||||
124
src/utils/rw_lock.hpp
Normal file
124
src/utils/rw_lock.hpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/// @file
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
/// A wrapper around `pthread_rwlock_t`, useful because it is not possible to
|
||||||
|
/// choose read or write priority for `std::shared_mutex`.
|
||||||
|
class RWLock {
|
||||||
|
public:
|
||||||
|
/// By passing the appropriate parameter to the `RWLock` constructor, it is
|
||||||
|
/// possible to control the behavior of `RWLock` while shared lock is held. If
|
||||||
|
/// the priority is set to `READ`, new shared (read) locks can be obtained
|
||||||
|
/// even though there is a thread waiting for an exclusive (write) lock, which
|
||||||
|
/// can lead to writer starvation. If the priority is set to `WRITE`, readers
|
||||||
|
/// will be blocked from obtaining new shared locks while there are writers
|
||||||
|
/// waiting, which can lead to reader starvation.
|
||||||
|
enum class Priority { READ, WRITE };
|
||||||
|
|
||||||
|
/// Construct a RWLock object with chosen priority. See comment above
|
||||||
|
/// `RWLockPriority` for details.
|
||||||
|
explicit RWLock(Priority priority) {
|
||||||
|
pthread_rwlockattr_t attr;
|
||||||
|
|
||||||
|
CHECK(pthread_rwlockattr_init(&attr) == 0)
|
||||||
|
<< "Couldn't initialize utils::RWLock!";
|
||||||
|
|
||||||
|
switch (priority) {
|
||||||
|
case Priority::READ:
|
||||||
|
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_READER_NP);
|
||||||
|
break;
|
||||||
|
case Priority::WRITE:
|
||||||
|
// There is also `PTHREAD_RWLOCK_PREFER_WRITER_NP` but it is not
|
||||||
|
// providing the desired behavior.
|
||||||
|
//
|
||||||
|
// From `man 7 pthread_rwlockattr_setkind_np`:
|
||||||
|
// "Setting the value read-write lock kind to
|
||||||
|
// PTHREAD_RWLOCK_PREFER_WRITER_NP results in the same behavior as
|
||||||
|
// setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a
|
||||||
|
// reader thread holds the lock, the thread holding a write lock will be
|
||||||
|
// starved. Setting the lock kind to
|
||||||
|
// PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP allows writers to run,
|
||||||
|
// but, as the name implies a writer may not lock recursively."
|
||||||
|
//
|
||||||
|
// For this reason, `RWLock` should not be used recursively.
|
||||||
|
pthread_rwlockattr_setkind_np(
|
||||||
|
&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(pthread_rwlock_init(&lock_, &attr) == 0)
|
||||||
|
<< "Couldn't initialize utils::RWLock!";
|
||||||
|
pthread_rwlockattr_destroy(&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
RWLock(const RWLock &) = delete;
|
||||||
|
RWLock &operator=(const RWLock &) = delete;
|
||||||
|
RWLock(RWLock &&) = delete;
|
||||||
|
RWLock &operator=(RWLock &&) = delete;
|
||||||
|
|
||||||
|
~RWLock() { pthread_rwlock_destroy(&lock_); }
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
CHECK(pthread_rwlock_wrlock(&lock_) == 0) << "Couldn't lock utils::RWLock!";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock() {
|
||||||
|
int err = pthread_rwlock_trywrlock(&lock_);
|
||||||
|
if (err == 0) return true;
|
||||||
|
CHECK(err == EBUSY) << "Couldn't try lock utils::RWLock!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
CHECK(pthread_rwlock_unlock(&lock_) == 0)
|
||||||
|
<< "Couldn't unlock utils::RWLock!";
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock_shared() {
|
||||||
|
int err;
|
||||||
|
while (true) {
|
||||||
|
err = pthread_rwlock_rdlock(&lock_);
|
||||||
|
if (err == 0) {
|
||||||
|
return;
|
||||||
|
} else if (err == EAGAIN) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
LOG(FATAL) << "Couldn't lock shared utils::RWLock!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock_shared() {
|
||||||
|
int err;
|
||||||
|
while (true) {
|
||||||
|
err = pthread_rwlock_tryrdlock(&lock_);
|
||||||
|
if (err == 0) {
|
||||||
|
return true;
|
||||||
|
} else if (err == EBUSY) {
|
||||||
|
return false;
|
||||||
|
} else if (err == EAGAIN) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
LOG(FATAL) << "Couldn't try lock shared utils::RWLock!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock_shared() {
|
||||||
|
CHECK(pthread_rwlock_unlock(&lock_) == 0)
|
||||||
|
<< "Couldn't unlock shared utils::RWLock!";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_rwlock_t lock_ = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
@@ -575,16 +575,13 @@ class SkipList final {
|
|||||||
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
|
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessor(const Accessor &other)
|
Accessor(const Accessor &) = delete;
|
||||||
: skiplist_(other.skiplist_), id_(skiplist_->gc_.AllocateId()) {}
|
Accessor &operator=(const Accessor &) = delete;
|
||||||
|
|
||||||
Accessor(Accessor &&other) noexcept
|
Accessor(Accessor &&other) noexcept
|
||||||
: skiplist_(other.skiplist_), id_(other.id_) {
|
: skiplist_(other.skiplist_), id_(other.id_) {
|
||||||
other.skiplist_ = nullptr;
|
other.skiplist_ = nullptr;
|
||||||
}
|
}
|
||||||
Accessor &operator=(const Accessor &other) {
|
|
||||||
skiplist_ = other.skiplist_;
|
|
||||||
id_ = skiplist_->gc_.AllocateId();
|
|
||||||
}
|
|
||||||
Accessor &operator=(Accessor &&other) noexcept {
|
Accessor &operator=(Accessor &&other) noexcept {
|
||||||
skiplist_ = other.skiplist_;
|
skiplist_ = other.skiplist_;
|
||||||
id_ = other.id_;
|
id_ = other.id_;
|
||||||
@@ -716,16 +713,13 @@ class SkipList final {
|
|||||||
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
|
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstAccessor(const ConstAccessor &other)
|
ConstAccessor(const ConstAccessor &) = delete;
|
||||||
: skiplist_(other.skiplist_), id_(skiplist_->gc_.AllocateId()) {}
|
ConstAccessor &operator=(const ConstAccessor &) = delete;
|
||||||
|
|
||||||
ConstAccessor(ConstAccessor &&other) noexcept
|
ConstAccessor(ConstAccessor &&other) noexcept
|
||||||
: skiplist_(other.skiplist_), id_(other.id_) {
|
: skiplist_(other.skiplist_), id_(other.id_) {
|
||||||
other.skiplist_ = nullptr;
|
other.skiplist_ = nullptr;
|
||||||
}
|
}
|
||||||
ConstAccessor &operator=(const ConstAccessor &other) {
|
|
||||||
skiplist_ = other.skiplist_;
|
|
||||||
id_ = skiplist_->gc_.AllocateId();
|
|
||||||
}
|
|
||||||
ConstAccessor &operator=(ConstAccessor &&other) noexcept {
|
ConstAccessor &operator=(ConstAccessor &&other) noexcept {
|
||||||
skiplist_ = other.skiplist_;
|
skiplist_ = other.skiplist_;
|
||||||
id_ = other.id_;
|
id_ = other.id_;
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
#include "utils/thread/sync.hpp"
|
|
||||||
|
|
||||||
#include <linux/futex.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
namespace sys {
|
|
||||||
inline int futex(void *addr1, int op, int val1, const struct timespec *timeout,
|
|
||||||
void *addr2, int val3) {
|
|
||||||
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sys
|
|
||||||
|
|
||||||
namespace utils {
|
|
||||||
|
|
||||||
RWLock::RWLock(RWLockPriority priority) {
|
|
||||||
int err;
|
|
||||||
pthread_rwlockattr_t attr;
|
|
||||||
|
|
||||||
err = pthread_rwlockattr_init(&attr);
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (priority) {
|
|
||||||
case RWLockPriority::READ:
|
|
||||||
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_READER_NP);
|
|
||||||
break;
|
|
||||||
case RWLockPriority::WRITE:
|
|
||||||
/* There is also `PTHREAD_RWLOCK_PREFER_WRITER_NP` but it is not
|
|
||||||
* providing the desired behavior.
|
|
||||||
*
|
|
||||||
* From `man 7 pthread_rwlockattr_setkind_np`:
|
|
||||||
* "Setting the value read-write lock kind to
|
|
||||||
* PTHREAD_RWLOCK_PREFER_WRITER_NP results in the same behavior as
|
|
||||||
* setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a
|
|
||||||
* reader thread holds the lock, the thread holding a write lock will be
|
|
||||||
* starved. Setting the lock kind to
|
|
||||||
* PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP allows writers to run,
|
|
||||||
* but, as the name implies a writer may not lock recursively."
|
|
||||||
*
|
|
||||||
* For this reason, `RWLock` should not be used recursively.
|
|
||||||
* */
|
|
||||||
pthread_rwlockattr_setkind_np(
|
|
||||||
&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pthread_rwlock_init(&lock_, &attr);
|
|
||||||
pthread_rwlockattr_destroy(&attr);
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RWLock::~RWLock() { pthread_rwlock_destroy(&lock_); }
|
|
||||||
|
|
||||||
void RWLock::lock() {
|
|
||||||
int err = pthread_rwlock_wrlock(&lock_);
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RWLock::try_lock() {
|
|
||||||
int err = pthread_rwlock_trywrlock(&lock_);
|
|
||||||
if (err == 0) return true;
|
|
||||||
if (err == EBUSY) return false;
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RWLock::unlock() {
|
|
||||||
int err = pthread_rwlock_unlock(&lock_);
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RWLock::lock_shared() {
|
|
||||||
int err = pthread_rwlock_rdlock(&lock_);
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RWLock::try_lock_shared() {
|
|
||||||
int err = pthread_rwlock_tryrdlock(&lock_);
|
|
||||||
if (err == 0) return true;
|
|
||||||
if (err == EBUSY) return false;
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RWLock::unlock_shared() {
|
|
||||||
int err = pthread_rwlock_unlock(&lock_);
|
|
||||||
if (err != 0) {
|
|
||||||
throw std::system_error(err, std::system_category());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Futex::lock(const struct timespec *timeout) {
|
|
||||||
// try to fast lock a few times before going to sleep
|
|
||||||
for (size_t i = 0; i < LOCK_RETRIES; ++i) {
|
|
||||||
// try to lock and exit if we succeed
|
|
||||||
if (try_lock()) return;
|
|
||||||
|
|
||||||
// we failed, chill a bit
|
|
||||||
relax();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the lock is contended, go to sleep. when someone
|
|
||||||
// wakes you up, try taking the lock again
|
|
||||||
while (mutex.all.exchange(LOCKED_CONTENDED, std::memory_order_seq_cst) &
|
|
||||||
LOCKED) {
|
|
||||||
// wait in the kernel for someone to wake us up when unlocking
|
|
||||||
auto status = futex_wait(LOCKED_CONTENDED, timeout);
|
|
||||||
|
|
||||||
// check if we woke up because of a timeout
|
|
||||||
if (status == -1 && errno == ETIMEDOUT)
|
|
||||||
throw LockTimeoutException("Lock timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Futex::unlock() {
|
|
||||||
futex_t state = LOCKED;
|
|
||||||
|
|
||||||
// if we're locked and uncontended, try to unlock the mutex before
|
|
||||||
// it becomes contended
|
|
||||||
if (mutex.all.load(std::memory_order_seq_cst) == LOCKED &&
|
|
||||||
mutex.all.compare_exchange_strong(state, UNLOCKED,
|
|
||||||
std::memory_order_seq_cst,
|
|
||||||
std::memory_order_seq_cst))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// we are contended, just release the lock
|
|
||||||
mutex.state.locked.store(UNLOCKED, std::memory_order_seq_cst);
|
|
||||||
|
|
||||||
// spin and hope someone takes a lock so we don't have to wake up
|
|
||||||
// anyone because that's quite expensive
|
|
||||||
for (size_t i = 0; i < UNLOCK_RETRIES; ++i) {
|
|
||||||
// if someone took the lock, we're ok
|
|
||||||
if (is_locked(std::memory_order_seq_cst)) return;
|
|
||||||
|
|
||||||
relax();
|
|
||||||
}
|
|
||||||
|
|
||||||
// store that we are becoming uncontended
|
|
||||||
mutex.state.contended.store(UNCONTENDED, std::memory_order_seq_cst);
|
|
||||||
|
|
||||||
// we need to wake someone up
|
|
||||||
futex_wake(LOCKED);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Futex::futex_wait(int value, const struct timespec *timeout) {
|
|
||||||
return sys::futex(&mutex.all, FUTEX_WAIT_PRIVATE, value, timeout, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Futex::futex_wake(int value) {
|
|
||||||
sys::futex(&mutex.all, FUTEX_WAKE_PRIVATE, value, nullptr, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace utils
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
/// @file
|
/// @file
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "utils/exceptions.hpp"
|
#include "utils/exceptions.hpp"
|
||||||
@@ -27,61 +22,6 @@ class LockTimeoutException : public BasicException {
|
|||||||
using BasicException::BasicException;
|
using BasicException::BasicException;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CasLock {
|
|
||||||
public:
|
|
||||||
void lock() {
|
|
||||||
bool locked = false;
|
|
||||||
|
|
||||||
while (!lock_flag.compare_exchange_weak(
|
|
||||||
locked, true, std::memory_order_release, std::memory_order_relaxed)) {
|
|
||||||
usleep(250);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() { lock_flag.store(0, std::memory_order_release); }
|
|
||||||
|
|
||||||
bool locked() { return lock_flag.load(std::memory_order_relaxed); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<bool> lock_flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// By passing the appropriate parameter to the `RWLock` constructor, it is
|
|
||||||
/// possible to control the behavior of `RWLock` while shared lock is held. If
|
|
||||||
/// the priority is set to `READ`, new shared (read) locks can be obtained even
|
|
||||||
/// though there is a thread waiting for an exclusive (write) lock, which can
|
|
||||||
/// lead to writer starvation. If the priority is set to `WRITE`, readers will
|
|
||||||
/// be blocked from obtaining new shared locks while there are writers waiting,
|
|
||||||
/// which can lead to reader starvation.
|
|
||||||
enum RWLockPriority { READ, WRITE };
|
|
||||||
|
|
||||||
/// A wrapper around `pthread_rwlock_t`, useful because it is not possible to
|
|
||||||
/// choose read or write priority for `std::shared_mutex`.
|
|
||||||
class RWLock {
|
|
||||||
public:
|
|
||||||
RWLock(const RWLock &) = delete;
|
|
||||||
RWLock &operator=(const RWLock &) = delete;
|
|
||||||
RWLock(RWLock &&) = delete;
|
|
||||||
RWLock &operator=(RWLock &&) = delete;
|
|
||||||
|
|
||||||
/// Construct a RWLock object with chosen priority. See comment above
|
|
||||||
/// `RWLockPriority` for details.
|
|
||||||
explicit RWLock(RWLockPriority priority);
|
|
||||||
|
|
||||||
~RWLock();
|
|
||||||
|
|
||||||
bool try_lock();
|
|
||||||
void lock();
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
bool try_lock_shared();
|
|
||||||
void lock_shared();
|
|
||||||
void unlock_shared();
|
|
||||||
|
|
||||||
private:
|
|
||||||
pthread_rwlock_t lock_ = PTHREAD_RWLOCK_INITIALIZER;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Lockable is used as an custom implementation of a mutex mechanism.
|
/// Lockable is used as an custom implementation of a mutex mechanism.
|
||||||
///
|
///
|
||||||
/// It is implemented as a wrapper around std::lock_guard and std::unique_guard
|
/// It is implemented as a wrapper around std::lock_guard and std::unique_guard
|
||||||
@@ -104,78 +44,4 @@ class Lockable {
|
|||||||
mutable lock_t lock;
|
mutable lock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Futex {
|
|
||||||
using futex_t = uint32_t;
|
|
||||||
using flag_t = uint8_t;
|
|
||||||
|
|
||||||
/// Data structure for implementing fast mutexes
|
|
||||||
///
|
|
||||||
/// This structure is 4B wide, as required for futex system call where
|
|
||||||
/// the last two bytes are used for two flags - contended and locked,
|
|
||||||
/// respectively. Memory layout for the structure looks like this:
|
|
||||||
///
|
|
||||||
/// all
|
|
||||||
/// |---------------------------------|
|
|
||||||
/// 00000000 00000000 0000000C 0000000L
|
|
||||||
/// |------| |------|
|
|
||||||
/// contended locked
|
|
||||||
///
|
|
||||||
/// L marks the locked bit
|
|
||||||
/// C marks the contended bit
|
|
||||||
union mutex_t {
|
|
||||||
std::atomic<futex_t> all{0};
|
|
||||||
|
|
||||||
struct {
|
|
||||||
std::atomic<flag_t> locked;
|
|
||||||
std::atomic<flag_t> contended;
|
|
||||||
} state;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Contention : futex_t { UNCONTENDED = 0x0000, CONTENDED = 0x0100 };
|
|
||||||
|
|
||||||
enum State : futex_t {
|
|
||||||
UNLOCKED = 0x0000,
|
|
||||||
LOCKED = 0x0001,
|
|
||||||
UNLOCKED_CONTENDED = UNLOCKED | CONTENDED, // 0x0100
|
|
||||||
LOCKED_CONTENDED = LOCKED | CONTENDED // 0x0101
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr size_t LOCK_RETRIES = 100;
|
|
||||||
static constexpr size_t UNLOCK_RETRIES = 200;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Futex() {
|
|
||||||
static_assert(sizeof(mutex_t) == sizeof(futex_t),
|
|
||||||
"Atomic futex should be the same size as non_atomic");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_lock() {
|
|
||||||
// we took the lock if we stored the LOCKED state and previous
|
|
||||||
// state was UNLOCKED
|
|
||||||
return mutex.state.locked.exchange(LOCKED, std::memory_order_seq_cst) ==
|
|
||||||
UNLOCKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock(const struct timespec *timeout = nullptr);
|
|
||||||
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
bool is_locked(std::memory_order order = std::memory_order_seq_cst) const {
|
|
||||||
return mutex.state.locked.load(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_contended(std::memory_order order = std::memory_order_seq_cst) const {
|
|
||||||
return mutex.state.contended.load(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutex_t mutex;
|
|
||||||
|
|
||||||
int futex_wait(int value, const struct timespec *timeout = nullptr);
|
|
||||||
|
|
||||||
void futex_wake(int value);
|
|
||||||
|
|
||||||
void relax() { CpuRelax(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|||||||
@@ -23,6 +23,3 @@ add_subdirectory(property_based)
|
|||||||
|
|
||||||
# integration test binaries
|
# integration test binaries
|
||||||
add_subdirectory(integration)
|
add_subdirectory(integration)
|
||||||
|
|
||||||
# feature benchmark test binaries
|
|
||||||
add_subdirectory(feature_benchmark)
|
|
||||||
|
|||||||
@@ -54,12 +54,6 @@ target_link_libraries(${test_prefix}edge_storage mg-single-node kvstore_dummy_li
|
|||||||
add_benchmark(mvcc.cpp)
|
add_benchmark(mvcc.cpp)
|
||||||
target_link_libraries(${test_prefix}mvcc mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}mvcc mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_benchmark(rpc.cpp)
|
|
||||||
target_link_libraries(${test_prefix}rpc mg-comm-rpc)
|
|
||||||
|
|
||||||
add_benchmark(serialization.cpp)
|
|
||||||
target_link_libraries(${test_prefix}serialization mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_benchmark(skip_list_random.cpp)
|
add_benchmark(skip_list_random.cpp)
|
||||||
target_link_libraries(${test_prefix}skip_list_random mg-utils)
|
target_link_libraries(${test_prefix}skip_list_random mg-utils)
|
||||||
|
|
||||||
@@ -77,6 +71,3 @@ target_link_libraries(${test_prefix}tx_engine mg-single-node kvstore_dummy_lib)
|
|||||||
|
|
||||||
add_benchmark(expansion.cpp)
|
add_benchmark(expansion.cpp)
|
||||||
target_link_libraries(${test_prefix}expansion mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}expansion mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_benchmark(storage_v2_gc.cpp)
|
|
||||||
target_link_libraries(${test_prefix}storage_v2_gc mg-storage-v2)
|
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Match)(benchmark::State &state) {
|
|||||||
auto dba = db_->Access();
|
auto dba = db_->Access();
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
ResultStreamFaker<query::TypedValue> results;
|
ResultStreamFaker<query::TypedValue> results;
|
||||||
interpreter()(query, dba, {}, false).PullAll(results);
|
interpreter()(query, dba, {}, false, utils::NewDeleteResource())
|
||||||
|
.PullAll(results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +61,8 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Expand)(benchmark::State &state) {
|
|||||||
auto dba = db_->Access();
|
auto dba = db_->Access();
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
ResultStreamFaker<query::TypedValue> results;
|
ResultStreamFaker<query::TypedValue> results;
|
||||||
interpreter()(query, dba, {}, false).PullAll(results);
|
interpreter()(query, dba, {}, false, utils::NewDeleteResource())
|
||||||
|
.PullAll(results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
#include "query/interpret/eval.hpp"
|
#include "query/interpret/eval.hpp"
|
||||||
|
#include "query/transaction_engine.hpp"
|
||||||
|
|
||||||
// The following classes are wrappers for utils::MemoryResource, so that we can
|
// The following classes are wrappers for utils::MemoryResource, so that we can
|
||||||
// use BENCHMARK_TEMPLATE
|
// use BENCHMARK_TEMPLATE
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
#include "database/graph_db.hpp"
|
#include "database/graph_db.hpp"
|
||||||
#include "database/graph_db_accessor.hpp"
|
#include "database/graph_db_accessor.hpp"
|
||||||
#include "query/frontend/opencypher/parser.hpp"
|
#include "query/frontend/opencypher/parser.hpp"
|
||||||
#include "query/frontend/semantic/required_privileges.hpp"
|
|
||||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
#include "query/plan/planner.hpp"
|
#include "query/plan/planner.hpp"
|
||||||
|
#include "query/transaction_engine.hpp"
|
||||||
|
|
||||||
// The following classes are wrappers for utils::MemoryResource, so that we can
|
// The following classes are wrappers for utils::MemoryResource, so that we can
|
||||||
// use BENCHMARK_TEMPLATE
|
// use BENCHMARK_TEMPLATE
|
||||||
@@ -31,6 +31,15 @@ class NewDeleteResource final {
|
|||||||
void Reset() {}
|
void Reset() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PoolResource final {
|
||||||
|
utils::PoolResource memory_{128, 4 * 1024};
|
||||||
|
|
||||||
|
public:
|
||||||
|
utils::MemoryResource *get() { return &memory_; }
|
||||||
|
|
||||||
|
void Reset() { memory_.Release(); }
|
||||||
|
};
|
||||||
|
|
||||||
static void AddVertices(database::GraphDb *db, int vertex_count) {
|
static void AddVertices(database::GraphDb *db, int vertex_count) {
|
||||||
auto dba = db->Access();
|
auto dba = db->Access();
|
||||||
for (int i = 0; i < vertex_count; i++) dba.InsertVertex();
|
for (int i = 0; i < vertex_count; i++) dba.InsertVertex();
|
||||||
@@ -82,8 +91,7 @@ static query::CypherQuery *ParseCypherQuery(const std::string &query_string,
|
|||||||
query::frontend::CypherMainVisitor cypher_visitor(parsing_context, ast);
|
query::frontend::CypherMainVisitor cypher_visitor(parsing_context, ast);
|
||||||
cypher_visitor.visit(parser.tree());
|
cypher_visitor.visit(parser.tree());
|
||||||
query::Interpreter::ParsedQuery parsed_query{
|
query::Interpreter::ParsedQuery parsed_query{
|
||||||
cypher_visitor.query(),
|
cypher_visitor.query()};
|
||||||
query::GetRequiredPrivileges(cypher_visitor.query())};
|
|
||||||
return utils::Downcast<query::CypherQuery>(parsed_query.query);
|
return utils::Downcast<query::CypherQuery>(parsed_query.query);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,6 +133,10 @@ BENCHMARK_TEMPLATE(Distinct, MonotonicBufferResource)
|
|||||||
->Range(1024, 1U << 21U)
|
->Range(1024, 1U << 21U)
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(Distinct, PoolResource)
|
||||||
|
->Range(1024, 1U << 21U)
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
static query::plan::ExpandVariable MakeExpandVariable(
|
static query::plan::ExpandVariable MakeExpandVariable(
|
||||||
query::EdgeAtom::Type expand_type, query::SymbolTable *symbol_table) {
|
query::EdgeAtom::Type expand_type, query::SymbolTable *symbol_table) {
|
||||||
auto input_symbol = symbol_table->CreateSymbol("input", false);
|
auto input_symbol = symbol_table->CreateSymbol("input", false);
|
||||||
@@ -178,6 +190,10 @@ BENCHMARK_TEMPLATE(ExpandVariable, MonotonicBufferResource)
|
|||||||
->Ranges({{1, 1U << 5U}, {512, 1U << 13U}})
|
->Ranges({{1, 1U << 5U}, {512, 1U << 13U}})
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(ExpandVariable, PoolResource)
|
||||||
|
->Ranges({{1, 1U << 5U}, {512, 1U << 13U}})
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void ExpandBfs(benchmark::State &state) {
|
static void ExpandBfs(benchmark::State &state) {
|
||||||
@@ -214,6 +230,10 @@ BENCHMARK_TEMPLATE(ExpandBfs, MonotonicBufferResource)
|
|||||||
->Range(512, 1U << 19U)
|
->Range(512, 1U << 19U)
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(ExpandBfs, PoolResource)
|
||||||
|
->Range(512, 1U << 19U)
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void ExpandShortest(benchmark::State &state) {
|
static void ExpandShortest(benchmark::State &state) {
|
||||||
@@ -255,6 +275,10 @@ BENCHMARK_TEMPLATE(ExpandShortest, MonotonicBufferResource)
|
|||||||
->Range(512, 1U << 20U)
|
->Range(512, 1U << 20U)
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(ExpandShortest, PoolResource)
|
||||||
|
->Range(512, 1U << 20U)
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void ExpandWeightedShortest(benchmark::State &state) {
|
static void ExpandWeightedShortest(benchmark::State &state) {
|
||||||
@@ -300,6 +324,10 @@ BENCHMARK_TEMPLATE(ExpandWeightedShortest, MonotonicBufferResource)
|
|||||||
->Range(512, 1U << 20U)
|
->Range(512, 1U << 20U)
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(ExpandWeightedShortest, PoolResource)
|
||||||
|
->Range(512, 1U << 20U)
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void Accumulate(benchmark::State &state) {
|
static void Accumulate(benchmark::State &state) {
|
||||||
@@ -340,6 +368,10 @@ BENCHMARK_TEMPLATE(Accumulate, MonotonicBufferResource)
|
|||||||
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(Accumulate, PoolResource)
|
||||||
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void Aggregate(benchmark::State &state) {
|
static void Aggregate(benchmark::State &state) {
|
||||||
@@ -393,6 +425,10 @@ BENCHMARK_TEMPLATE(Aggregate, MonotonicBufferResource)
|
|||||||
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(Aggregate, PoolResource)
|
||||||
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void OrderBy(benchmark::State &state) {
|
static void OrderBy(benchmark::State &state) {
|
||||||
@@ -439,6 +475,10 @@ BENCHMARK_TEMPLATE(OrderBy, MonotonicBufferResource)
|
|||||||
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(OrderBy, PoolResource)
|
||||||
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
template <class TMemory>
|
template <class TMemory>
|
||||||
// NOLINTNEXTLINE(google-runtime-references)
|
// NOLINTNEXTLINE(google-runtime-references)
|
||||||
static void Unwind(benchmark::State &state) {
|
static void Unwind(benchmark::State &state) {
|
||||||
@@ -478,4 +518,8 @@ BENCHMARK_TEMPLATE(Unwind, MonotonicBufferResource)
|
|||||||
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
->Unit(benchmark::kMicrosecond);
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(Unwind, PoolResource)
|
||||||
|
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
|
||||||
|
->Unit(benchmark::kMicrosecond);
|
||||||
|
|
||||||
BENCHMARK_MAIN();
|
BENCHMARK_MAIN();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
#include "utils/memory.hpp"
|
||||||
#include "utils/skip_list.hpp"
|
#include "utils/skip_list.hpp"
|
||||||
#include "utils/spin_lock.hpp"
|
#include "utils/spin_lock.hpp"
|
||||||
|
|
||||||
@@ -80,6 +81,42 @@ BENCHMARK_REGISTER_F(StdSetInsertFixture, Insert)
|
|||||||
->Unit(benchmark::kNanosecond)
|
->Unit(benchmark::kNanosecond)
|
||||||
->UseRealTime();
|
->UseRealTime();
|
||||||
|
|
||||||
|
class StdSetWithPoolAllocatorInsertFixture : public benchmark::Fixture {
|
||||||
|
protected:
|
||||||
|
void SetUp(const benchmark::State &state) override {
|
||||||
|
if (state.thread_index == 0) {
|
||||||
|
container.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::PoolResource memory_{256U /* max_blocks_per_chunk */,
|
||||||
|
1024U /* max_block_size */,
|
||||||
|
utils::NewDeleteResource()};
|
||||||
|
std::set<uint64_t, std::less<>, utils::Allocator<uint64_t>> container{
|
||||||
|
&memory_};
|
||||||
|
utils::SpinLock lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(StdSetWithPoolAllocatorInsertFixture, Insert)
|
||||||
|
(benchmark::State &state) {
|
||||||
|
std::mt19937 gen(state.thread_index);
|
||||||
|
std::uniform_int_distribution<uint64_t> dist(0, kMaxNum);
|
||||||
|
uint64_t counter = 0;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
std::lock_guard<utils::SpinLock> guard(lock);
|
||||||
|
if (container.insert(dist(gen)).second) {
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetItemsProcessed(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(StdSetWithPoolAllocatorInsertFixture, Insert)
|
||||||
|
->ThreadRange(1, kThreadsNum)
|
||||||
|
->Unit(benchmark::kNanosecond)
|
||||||
|
->UseRealTime();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// utils::SkipList set Find
|
// utils::SkipList set Find
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -154,6 +191,44 @@ BENCHMARK_REGISTER_F(StdSetFindFixture, Find)
|
|||||||
->Unit(benchmark::kNanosecond)
|
->Unit(benchmark::kNanosecond)
|
||||||
->UseRealTime();
|
->UseRealTime();
|
||||||
|
|
||||||
|
class StdSetWithPoolAllocatorFindFixture : public benchmark::Fixture {
|
||||||
|
protected:
|
||||||
|
void SetUp(const benchmark::State &state) override {
|
||||||
|
if (state.thread_index == 0 && container.size() == 0) {
|
||||||
|
for (uint64_t i = 0; i < kMaxNum; ++i) {
|
||||||
|
container.insert(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::PoolResource memory_{256U /* max_blocks_per_chunk */,
|
||||||
|
1024U /* max_block_size */,
|
||||||
|
utils::NewDeleteResource()};
|
||||||
|
std::set<uint64_t, std::less<>, utils::Allocator<uint64_t>> container{
|
||||||
|
&memory_};
|
||||||
|
utils::SpinLock lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(StdSetWithPoolAllocatorFindFixture, Find)
|
||||||
|
(benchmark::State &state) {
|
||||||
|
std::mt19937 gen(state.thread_index);
|
||||||
|
std::uniform_int_distribution<uint64_t> dist(0, kMaxNum);
|
||||||
|
uint64_t counter = 0;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
std::lock_guard<utils::SpinLock> guard(lock);
|
||||||
|
if (container.find(dist(gen)) != container.end()) {
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetItemsProcessed(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(StdSetWithPoolAllocatorFindFixture, Find)
|
||||||
|
->ThreadRange(1, kThreadsNum)
|
||||||
|
->Unit(benchmark::kNanosecond)
|
||||||
|
->UseRealTime();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Map tests common
|
// Map tests common
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -178,7 +253,7 @@ class SkipListMapInsertFixture : public benchmark::Fixture {
|
|||||||
protected:
|
protected:
|
||||||
void SetUp(const benchmark::State &state) override {
|
void SetUp(const benchmark::State &state) override {
|
||||||
if (state.thread_index == 0) {
|
if (state.thread_index == 0) {
|
||||||
list = utils::SkipList<MapObject>();;
|
list = utils::SkipList<MapObject>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +314,43 @@ BENCHMARK_REGISTER_F(StdMapInsertFixture, Insert)
|
|||||||
->Unit(benchmark::kNanosecond)
|
->Unit(benchmark::kNanosecond)
|
||||||
->UseRealTime();
|
->UseRealTime();
|
||||||
|
|
||||||
|
class StdMapWithPoolAllocatorInsertFixture : public benchmark::Fixture {
|
||||||
|
protected:
|
||||||
|
void SetUp(const benchmark::State &state) override {
|
||||||
|
if (state.thread_index == 0) {
|
||||||
|
container = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::PoolResource memory_{256U /* max_blocks_per_chunk */,
|
||||||
|
1024U /* max_block_size */,
|
||||||
|
utils::NewDeleteResource()};
|
||||||
|
std::map<uint64_t, uint64_t, std::less<>,
|
||||||
|
utils::Allocator<std::pair<uint64_t, uint64_t>>>
|
||||||
|
container{&memory_};
|
||||||
|
utils::SpinLock lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(StdMapWithPoolAllocatorInsertFixture, Insert)
|
||||||
|
(benchmark::State &state) {
|
||||||
|
std::mt19937 gen(state.thread_index);
|
||||||
|
std::uniform_int_distribution<uint64_t> dist(0, kMaxNum);
|
||||||
|
uint64_t counter = 0;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
std::lock_guard<utils::SpinLock> guard(lock);
|
||||||
|
if (container.insert({dist(gen), 0}).second) {
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetItemsProcessed(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(StdMapWithPoolAllocatorInsertFixture, Insert)
|
||||||
|
->ThreadRange(1, kThreadsNum)
|
||||||
|
->Unit(benchmark::kNanosecond)
|
||||||
|
->UseRealTime();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// utils::SkipList map Find
|
// utils::SkipList map Find
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -313,4 +425,43 @@ BENCHMARK_REGISTER_F(StdMapFindFixture, Find)
|
|||||||
->Unit(benchmark::kNanosecond)
|
->Unit(benchmark::kNanosecond)
|
||||||
->UseRealTime();
|
->UseRealTime();
|
||||||
|
|
||||||
|
class StdMapWithPoolAllocatorFindFixture : public benchmark::Fixture {
|
||||||
|
protected:
|
||||||
|
void SetUp(const benchmark::State &state) override {
|
||||||
|
if (state.thread_index == 0 && container.size() == 0) {
|
||||||
|
for (uint64_t i = 0; i < kMaxNum; ++i) {
|
||||||
|
container.insert({i, 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::PoolResource memory_{256U /* max_blocks_per_chunk */,
|
||||||
|
1024U /* max_block_size */,
|
||||||
|
utils::NewDeleteResource()};
|
||||||
|
std::map<uint64_t, uint64_t, std::less<>,
|
||||||
|
utils::Allocator<std::pair<uint64_t, uint64_t>>>
|
||||||
|
container{&memory_};
|
||||||
|
utils::SpinLock lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(StdMapWithPoolAllocatorFindFixture, Find)
|
||||||
|
(benchmark::State &state) {
|
||||||
|
std::mt19937 gen(state.thread_index);
|
||||||
|
std::uniform_int_distribution<uint64_t> dist(0, kMaxNum);
|
||||||
|
uint64_t counter = 0;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
std::lock_guard<utils::SpinLock> guard(lock);
|
||||||
|
if (container.find(dist(gen)) != container.end()) {
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.SetItemsProcessed(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(StdMapWithPoolAllocatorFindFixture, Find)
|
||||||
|
->ThreadRange(1, kThreadsNum)
|
||||||
|
->Unit(benchmark::kNanosecond)
|
||||||
|
->UseRealTime();
|
||||||
|
|
||||||
BENCHMARK_MAIN();
|
BENCHMARK_MAIN();
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ target_link_libraries(${test_prefix}dynamic_bitset_set mg-single-node kvstore_du
|
|||||||
add_concurrent_test(dynamic_bitset_set_n.cpp)
|
add_concurrent_test(dynamic_bitset_set_n.cpp)
|
||||||
target_link_libraries(${test_prefix}dynamic_bitset_set_n mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}dynamic_bitset_set_n mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_concurrent_test(futex.cpp)
|
|
||||||
target_link_libraries(${test_prefix}futex mg-single-node kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_concurrent_test(network_read_hang.cpp)
|
add_concurrent_test(network_read_hang.cpp)
|
||||||
target_link_libraries(${test_prefix}network_read_hang mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}network_read_hang mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
|
||||||
#include <random>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
#include "utils/thread/sync.hpp"
|
|
||||||
|
|
||||||
utils::Futex futex;
|
|
||||||
int x = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param thread id
|
|
||||||
*/
|
|
||||||
void test_lock(int) {
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 gen(rd());
|
|
||||||
std::uniform_int_distribution<> dis(0, 1000);
|
|
||||||
|
|
||||||
// TODO: create long running test
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
|
||||||
{
|
|
||||||
std::unique_lock<utils::Futex> guard(futex);
|
|
||||||
x++;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
|
||||||
CHECK(x == 1) << "Other thread shouldn't be able to "
|
|
||||||
"change the value of x";
|
|
||||||
x--;
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
constexpr int N = 16;
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
|
|
||||||
for (int i = 0; i < N; ++i) threads.push_back(std::thread(test_lock, i));
|
|
||||||
|
|
||||||
for (auto& thread : threads) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from card_fraud import NUM_MACHINES, BINARIES
|
|
||||||
|
|
||||||
# paths
|
|
||||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
WORKSPACE_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "..", ".."))
|
|
||||||
OUTPUT_DIR_REL = os.path.join(os.path.relpath(SCRIPT_DIR, WORKSPACE_DIR), "output")
|
|
||||||
|
|
||||||
# generate runs
|
|
||||||
runs = []
|
|
||||||
|
|
||||||
binaries = list(map(lambda x: os.path.join("..", "..", "build_release", x), BINARIES))
|
|
||||||
|
|
||||||
for i in range(NUM_MACHINES):
|
|
||||||
name = "master" if i == 0 else "worker" + str(i)
|
|
||||||
additional = ["master.py"] if i == 0 else []
|
|
||||||
outfile_paths = ["\\./" + OUTPUT_DIR_REL + "/.+"] if i == 0 else []
|
|
||||||
if i == 0:
|
|
||||||
cmd = "master.py"
|
|
||||||
args = "--machines-num {0} --test-suite card_fraud " \
|
|
||||||
"--test card_fraud".format(NUM_MACHINES)
|
|
||||||
else:
|
|
||||||
cmd = "jail_service.py"
|
|
||||||
args = ""
|
|
||||||
runs.append({
|
|
||||||
"name": "distributed__card_fraud__" + name,
|
|
||||||
"cd": "..",
|
|
||||||
"supervisor": cmd,
|
|
||||||
"arguments": args,
|
|
||||||
"infiles": binaries + [
|
|
||||||
"common.py",
|
|
||||||
"jail_service.py",
|
|
||||||
"card_fraud/card_fraud.py",
|
|
||||||
"card_fraud/snapshots/worker_" + str(i),
|
|
||||||
] + additional,
|
|
||||||
"outfile_paths": outfile_paths,
|
|
||||||
"parallel_run": "distributed__card_fraud",
|
|
||||||
"enable_network": True,
|
|
||||||
})
|
|
||||||
|
|
||||||
#print(json.dumps(runs, indent=4, sort_keys=True))
|
|
||||||
print("[]")
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
- name: feature_benchmark__kafka
|
|
||||||
cd: kafka
|
|
||||||
commands: ./runner.sh
|
|
||||||
infiles:
|
|
||||||
- runner.sh # runner script
|
|
||||||
- transform.py # transform script
|
|
||||||
- generate.py # dataset generator script
|
|
||||||
- ../../../build_release/tests/feature_benchmark/kafka/kafka.py # kafka script
|
|
||||||
- ../../../build_release/tests/feature_benchmark/kafka/benchmark # benchmark binary
|
|
||||||
enable_network: true
|
|
||||||
|
|
||||||
- name: feature_benchmark__ha__read
|
|
||||||
cd: ha/read
|
|
||||||
commands: ./runner.sh
|
|
||||||
infiles:
|
|
||||||
- runner.sh # runner script
|
|
||||||
- raft.json # raft configuration file
|
|
||||||
- coordination.json # coordination configuration file
|
|
||||||
- ../../../../build_release/tests/feature_benchmark/ha/read/benchmark # benchmark binary
|
|
||||||
- ../../../../build_release/memgraph_ha # memgraph binary
|
|
||||||
enable_network: true
|
|
||||||
|
|
||||||
- name: feature_benchmark__ha__write
|
|
||||||
cd: ha/write
|
|
||||||
commands: ./runner.sh
|
|
||||||
infiles:
|
|
||||||
- runner.sh # runner script
|
|
||||||
- raft.json # raft configuration file
|
|
||||||
- coordination.json # coordination configuration file
|
|
||||||
- ../../../../build_release/tests/feature_benchmark/ha/write/benchmark # benchmark binary
|
|
||||||
- ../../../../build_release/memgraph_ha # memgraph binary
|
|
||||||
enable_network: true
|
|
||||||
@@ -6,36 +6,3 @@ add_subdirectory(ssl)
|
|||||||
|
|
||||||
# transactions test binaries
|
# transactions test binaries
|
||||||
add_subdirectory(transactions)
|
add_subdirectory(transactions)
|
||||||
|
|
||||||
# kafka test binaries
|
|
||||||
add_subdirectory(kafka)
|
|
||||||
|
|
||||||
# auth test binaries
|
|
||||||
add_subdirectory(auth)
|
|
||||||
|
|
||||||
# distributed test binaries
|
|
||||||
add_subdirectory(distributed)
|
|
||||||
|
|
||||||
# distributed ha/basic binaries
|
|
||||||
add_subdirectory(ha/basic)
|
|
||||||
|
|
||||||
# distributed ha/constraints binaries
|
|
||||||
add_subdirectory(ha/constraints)
|
|
||||||
|
|
||||||
# distributed ha/index binaries
|
|
||||||
add_subdirectory(ha/index)
|
|
||||||
|
|
||||||
# distributed ha/large_log_entries binaries
|
|
||||||
add_subdirectory(ha/large_log_entries)
|
|
||||||
|
|
||||||
# distributed ha/leader_election binaries
|
|
||||||
add_subdirectory(ha/leader_election)
|
|
||||||
|
|
||||||
# distributed ha/term_updates binaries
|
|
||||||
add_subdirectory(ha/term_updates)
|
|
||||||
|
|
||||||
# audit test binaries
|
|
||||||
add_subdirectory(audit)
|
|
||||||
|
|
||||||
# ldap test binaries
|
|
||||||
add_subdirectory(ldap)
|
|
||||||
|
|||||||
@@ -22,122 +22,3 @@
|
|||||||
- runner.sh # runner script
|
- runner.sh # runner script
|
||||||
- ../../../build_debug/memgraph # memgraph binary
|
- ../../../build_debug/memgraph # memgraph binary
|
||||||
- ../../../build_debug/tests/integration/transactions/tester # tester binary
|
- ../../../build_debug/tests/integration/transactions/tester # tester binary
|
||||||
|
|
||||||
- name: integration__kafka
|
|
||||||
cd: kafka
|
|
||||||
commands: ./runner.sh
|
|
||||||
infiles:
|
|
||||||
- runner.sh # runner script
|
|
||||||
- transform.py # transform script
|
|
||||||
- ../../../build_debug/memgraph # memgraph binary
|
|
||||||
- ../../../build_debug/kafka.py # kafka script
|
|
||||||
- ../../../build_debug/tests/integration/kafka/tester # tester binary
|
|
||||||
enable_network: true
|
|
||||||
|
|
||||||
- name: integration__auth
|
|
||||||
cd: auth
|
|
||||||
commands: TIMEOUT=820 ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- ../../../build_debug/memgraph # memgraph binary
|
|
||||||
- ../../../build_debug/tests/integration/auth/checker # checker binary
|
|
||||||
- ../../../build_debug/tests/integration/auth/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__audit
|
|
||||||
cd: audit
|
|
||||||
commands: ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- ../../../build_debug/memgraph # memgraph binary
|
|
||||||
- ../../../build_debug/tests/integration/audit/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ldap
|
|
||||||
cd: ldap
|
|
||||||
commands: |
|
|
||||||
./prepare.sh
|
|
||||||
./runner.py
|
|
||||||
infiles:
|
|
||||||
- prepare.sh # preparation script
|
|
||||||
- runner.py # runner script
|
|
||||||
- schema.ldif # schema file
|
|
||||||
- ../../../build_debug/memgraph # memgraph binary
|
|
||||||
- ../../../build_debug/tests/integration/ldap/tester # tester binary
|
|
||||||
enable_network: true
|
|
||||||
|
|
||||||
- name: integration__distributed
|
|
||||||
cd: distributed
|
|
||||||
commands: TIMEOUT=480 ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- ../../../build_debug/memgraph_distributed # memgraph distributed binary
|
|
||||||
- ../../../build_debug/tests/integration/distributed/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_basic
|
|
||||||
cd: ha/basic
|
|
||||||
commands: TIMEOUT=480 ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/basic/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_constraints
|
|
||||||
cd: ha/constraints
|
|
||||||
commands: ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/constraints/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_index
|
|
||||||
cd: ha/index
|
|
||||||
commands: ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/index/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_large_log_entries
|
|
||||||
cd: ha/large_log_entries
|
|
||||||
commands: TIMEOUT=600 ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/large_log_entries/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_leader_election
|
|
||||||
cd: ha/leader_election
|
|
||||||
commands: TIMEOUT=300 ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/leader_election/tester # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_log_compaction
|
|
||||||
cd: ha/log_compaction
|
|
||||||
commands: ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/manual/ha_client # tester binary
|
|
||||||
|
|
||||||
- name: integration__ha_term_updates
|
|
||||||
cd: ha/term_updates
|
|
||||||
commands: ./runner.py
|
|
||||||
infiles:
|
|
||||||
- runner.py # runner script
|
|
||||||
- raft.json # raft configuration
|
|
||||||
- ../ha_test.py # raft test base module
|
|
||||||
- ../../../../build_debug/memgraph_ha # memgraph ha binary
|
|
||||||
- ../../../../build_debug/tests/integration/ha/term_updates/tester # tester binary
|
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ function(add_macro_benchmark test_cpp)
|
|||||||
endfunction(add_macro_benchmark)
|
endfunction(add_macro_benchmark)
|
||||||
|
|
||||||
add_macro_benchmark(clients/pokec_client.cpp)
|
add_macro_benchmark(clients/pokec_client.cpp)
|
||||||
target_link_libraries(${test_prefix}pokec_client mg-communication mg-io mg-utils mg-stats json)
|
target_link_libraries(${test_prefix}pokec_client mg-communication mg-io mg-utils json)
|
||||||
|
|
||||||
add_macro_benchmark(clients/graph_500_bfs.cpp)
|
add_macro_benchmark(clients/graph_500_bfs.cpp)
|
||||||
target_link_libraries(${test_prefix}graph_500_bfs mg-communication mg-comm-rpc mg-io mg-utils mg-stats json)
|
target_link_libraries(${test_prefix}graph_500_bfs mg-communication mg-io mg-utils json)
|
||||||
|
|
||||||
add_macro_benchmark(clients/bfs_pokec_client.cpp)
|
add_macro_benchmark(clients/bfs_pokec_client.cpp)
|
||||||
target_link_libraries(${test_prefix}bfs_pokec_client mg-communication mg-io mg-utils mg-stats json)
|
target_link_libraries(${test_prefix}bfs_pokec_client mg-communication mg-io mg-utils json)
|
||||||
|
|
||||||
add_macro_benchmark(clients/query_client.cpp)
|
add_macro_benchmark(clients/query_client.cpp)
|
||||||
target_link_libraries(${test_prefix}query_client mg-communication mg-io mg-utils)
|
target_link_libraries(${test_prefix}query_client mg-communication mg-io mg-utils)
|
||||||
|
|
||||||
add_macro_benchmark(clients/card_fraud_client.cpp)
|
add_macro_benchmark(clients/card_fraud_client.cpp)
|
||||||
target_link_libraries(${test_prefix}card_fraud_client mg-communication mg-comm-rpc mg-io mg-utils mg-stats json)
|
target_link_libraries(${test_prefix}card_fraud_client mg-communication mg-io mg-utils json)
|
||||||
|
|||||||
@@ -5,10 +5,7 @@
|
|||||||
|
|
||||||
#include "gflags/gflags.h"
|
#include "gflags/gflags.h"
|
||||||
|
|
||||||
#include "communication/rpc/client.hpp"
|
#include "utils/rw_lock.hpp"
|
||||||
#include "stats/stats.hpp"
|
|
||||||
#include "stats/stats_rpc_messages.hpp"
|
|
||||||
#include "utils/thread/sync.hpp"
|
|
||||||
|
|
||||||
#include "long_running_common.hpp"
|
#include "long_running_common.hpp"
|
||||||
|
|
||||||
@@ -17,20 +14,12 @@ std::atomic<int64_t> num_cards;
|
|||||||
std::atomic<int64_t> num_transactions;
|
std::atomic<int64_t> num_transactions;
|
||||||
std::atomic<int64_t> max_tx_id;
|
std::atomic<int64_t> max_tx_id;
|
||||||
|
|
||||||
utils::RWLock world_lock(utils::RWLockPriority::WRITE);
|
utils::RWLock world_lock(utils::RWLock::Priority::WRITE);
|
||||||
|
|
||||||
DEFINE_string(config, "", "test config");
|
DEFINE_string(config, "", "test config");
|
||||||
|
|
||||||
enum class Role { WORKER, ANALYTIC, CLEANUP };
|
enum class Role { WORKER, ANALYTIC, CLEANUP };
|
||||||
|
|
||||||
stats::Gauge &num_vertices = stats::GetGauge("vertices");
|
|
||||||
stats::Gauge &num_edges = stats::GetGauge("edges");
|
|
||||||
|
|
||||||
void UpdateStats() {
|
|
||||||
num_vertices.Set(num_pos + num_cards + num_transactions);
|
|
||||||
num_edges.Set(2 * num_transactions);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t NumNodesWithLabel(Client &client, std::string label) {
|
int64_t NumNodesWithLabel(Client &client, std::string label) {
|
||||||
std::string query = fmt::format("MATCH (u :{}) RETURN count(u)", label);
|
std::string query = fmt::format("MATCH (u :{}) RETURN count(u)", label);
|
||||||
auto result = ExecuteNTimesTillSuccess(client, query, {}, MAX_RETRIES);
|
auto result = ExecuteNTimesTillSuccess(client, query, {}, MAX_RETRIES);
|
||||||
@@ -176,7 +165,6 @@ class CardFraudClient : public TestClient {
|
|||||||
card_id, tx_id, pos_id);
|
card_id, tx_id, pos_id);
|
||||||
|
|
||||||
num_transactions++;
|
num_transactions++;
|
||||||
UpdateStats();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t UniformInt(int64_t a, int64_t b) {
|
int64_t UniformInt(int64_t a, int64_t b) {
|
||||||
@@ -261,7 +249,6 @@ class CardFraudClient : public TestClient {
|
|||||||
num_transactions, num_transactions_db, deleted,
|
num_transactions, num_transactions_db, deleted,
|
||||||
num_transactions - num_transactions_db);
|
num_transactions - num_transactions_db);
|
||||||
num_transactions = num_transactions_db;
|
num_transactions = num_transactions_db;
|
||||||
UpdateStats();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(
|
std::this_thread::sleep_for(
|
||||||
@@ -334,9 +321,6 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
communication::Init();
|
communication::Init();
|
||||||
|
|
||||||
stats::InitStatsLogging(
|
|
||||||
fmt::format("client.long_running.{}.{}", FLAGS_group, FLAGS_scenario));
|
|
||||||
|
|
||||||
Endpoint endpoint(FLAGS_address, FLAGS_port);
|
Endpoint endpoint(FLAGS_address, FLAGS_port);
|
||||||
ClientContext context(FLAGS_use_ssl);
|
ClientContext context(FLAGS_use_ssl);
|
||||||
Client client(&context);
|
Client client(&context);
|
||||||
@@ -382,7 +366,5 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
RunMultithreadedTest(clients);
|
RunMultithreadedTest(clients);
|
||||||
|
|
||||||
stats::StopStatsLogging();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
#include "gflags/gflags.h"
|
#include "gflags/gflags.h"
|
||||||
|
|
||||||
#include "long_running_common.hpp"
|
#include "long_running_common.hpp"
|
||||||
#include "stats/stats.hpp"
|
|
||||||
#include "stats/stats_rpc_messages.hpp"
|
|
||||||
|
|
||||||
class Graph500BfsClient : public TestClient {
|
class Graph500BfsClient : public TestClient {
|
||||||
public:
|
public:
|
||||||
@@ -55,7 +53,5 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
RunMultithreadedTest(clients);
|
RunMultithreadedTest(clients);
|
||||||
|
|
||||||
stats::StopStatsLogging();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
#include "json/json.hpp"
|
#include "json/json.hpp"
|
||||||
|
|
||||||
#include "stats/metrics.hpp"
|
|
||||||
#include "stats/stats.hpp"
|
|
||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
@@ -35,9 +33,9 @@ DEFINE_int32(duration, 30, "Number of seconds to execute benchmark");
|
|||||||
DEFINE_string(group, "unknown", "Test group name");
|
DEFINE_string(group, "unknown", "Test group name");
|
||||||
DEFINE_string(scenario, "unknown", "Test scenario name");
|
DEFINE_string(scenario, "unknown", "Test scenario name");
|
||||||
|
|
||||||
auto &executed_queries = stats::GetCounter("executed_queries");
|
std::atomic<uint64_t> executed_queries;
|
||||||
auto &executed_steps = stats::GetCounter("executed_steps");
|
std::atomic<uint64_t> executed_steps;
|
||||||
auto &serialization_errors = stats::GetCounter("serialization_errors");
|
std::atomic<uint64_t> serialization_errors;
|
||||||
|
|
||||||
class TestClient {
|
class TestClient {
|
||||||
public:
|
public:
|
||||||
@@ -59,7 +57,7 @@ class TestClient {
|
|||||||
runner_thread_ = std::thread([&] {
|
runner_thread_ = std::thread([&] {
|
||||||
while (keep_running_) {
|
while (keep_running_) {
|
||||||
Step();
|
Step();
|
||||||
executed_steps.Bump();
|
++executed_steps;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -82,7 +80,7 @@ class TestClient {
|
|||||||
std::tie(result, retries) =
|
std::tie(result, retries) =
|
||||||
ExecuteNTimesTillSuccess(client_, query, params, MAX_RETRIES);
|
ExecuteNTimesTillSuccess(client_, query, params, MAX_RETRIES);
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
serialization_errors.Bump(MAX_RETRIES);
|
serialization_errors += MAX_RETRIES;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
auto wall_time = timer.Elapsed();
|
auto wall_time = timer.Elapsed();
|
||||||
@@ -96,8 +94,8 @@ class TestClient {
|
|||||||
stats_[query].push_back(std::move(metadata));
|
stats_[query].push_back(std::move(metadata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
executed_queries.Bump();
|
++executed_queries;
|
||||||
serialization_errors.Bump(retries);
|
serialization_errors += retries;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,16 +175,11 @@ void RunMultithreadedTest(std::vector<std::unique_ptr<TestClient>> &clients) {
|
|||||||
auto it = aggregated_query_stats.insert({stat.first, Value(0.0)}).first;
|
auto it = aggregated_query_stats.insert({stat.first, Value(0.0)}).first;
|
||||||
it->second = (it->second.ValueDouble() * old_count + stat.second) /
|
it->second = (it->second.ValueDouble() * old_count + stat.second) /
|
||||||
(old_count + new_count);
|
(old_count + new_count);
|
||||||
stats::LogStat(
|
|
||||||
fmt::format("queries.{}.{}", query_stats.first, stat.first),
|
|
||||||
(stat.second / new_count));
|
|
||||||
}
|
}
|
||||||
stats::LogStat(fmt::format("queries.{}.count", query_stats.first),
|
|
||||||
new_count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out << "{\"num_executed_queries\": " << executed_queries.Value() << ", "
|
out << "{\"num_executed_queries\": " << executed_queries << ", "
|
||||||
<< "\"num_executed_steps\": " << executed_steps.Value() << ", "
|
<< "\"num_executed_steps\": " << executed_steps << ", "
|
||||||
<< "\"elapsed_time\": " << timer.Elapsed().count()
|
<< "\"elapsed_time\": " << timer.Elapsed().count()
|
||||||
<< ", \"queries\": [";
|
<< ", \"queries\": [";
|
||||||
utils::PrintIterable(
|
utils::PrintIterable(
|
||||||
|
|||||||
@@ -30,35 +30,6 @@ target_link_libraries(${test_prefix}binomial mg-utils)
|
|||||||
add_manual_test(bolt_client.cpp)
|
add_manual_test(bolt_client.cpp)
|
||||||
target_link_libraries(${test_prefix}bolt_client mg-communication)
|
target_link_libraries(${test_prefix}bolt_client mg-communication)
|
||||||
|
|
||||||
add_manual_test(card_fraud_generate_snapshot.cpp)
|
|
||||||
target_link_libraries(${test_prefix}card_fraud_generate_snapshot mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_manual_test(card_fraud_local.cpp)
|
|
||||||
target_link_libraries(${test_prefix}card_fraud_local mg-distributed kvstore_dummy_lib gtest)
|
|
||||||
|
|
||||||
add_manual_test(distributed_query_planner.cpp interactive_planning.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_query_planner mg-distributed
|
|
||||||
kvstore_dummy_lib)
|
|
||||||
if (READLINE_FOUND)
|
|
||||||
target_link_libraries(${test_prefix}distributed_query_planner readline)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_manual_test(distributed_repl.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_repl mg-distributed kvstore_dummy_lib gtest readline)
|
|
||||||
|
|
||||||
add_manual_test(generate_snapshot.cpp)
|
|
||||||
target_link_libraries(${test_prefix}generate_snapshot mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_manual_test(graph_500_generate_snapshot.cpp)
|
|
||||||
target_link_libraries(${test_prefix}graph_500_generate_snapshot mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_manual_test(ha_client.cpp)
|
|
||||||
target_link_libraries(${test_prefix}ha_client mg-utils mg-communication)
|
|
||||||
|
|
||||||
add_manual_test(ha_proxy.cpp)
|
|
||||||
target_include_directories(${test_prefix}ha_proxy PRIVATE ${CMAKE_BINARY_DIR}/src)
|
|
||||||
target_link_libraries(${test_prefix}ha_proxy mg-utils mg-communication)
|
|
||||||
|
|
||||||
add_manual_test(kvstore_console.cpp)
|
add_manual_test(kvstore_console.cpp)
|
||||||
target_link_libraries(${test_prefix}kvstore_console kvstore_lib gflags glog)
|
target_link_libraries(${test_prefix}kvstore_console kvstore_lib gflags glog)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ int main(int argc, char *argv[]) {
|
|||||||
database::GraphDb db;
|
database::GraphDb db;
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
auto results = query::Interpreter()(argv[1], dba, {}, false);
|
auto results =
|
||||||
|
query::Interpreter()(argv[1], dba, {}, false, utils::NewDeleteResource());
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
|
|||||||
@@ -5,10 +5,3 @@
|
|||||||
- ../../build_release/memgraph # memgraph release binary
|
- ../../build_release/memgraph # memgraph release binary
|
||||||
outfile_paths: &OUTFILE_PATHS
|
outfile_paths: &OUTFILE_PATHS
|
||||||
- \./memgraph/tests/qa/\.quality_assurance_status
|
- \./memgraph/tests/qa/\.quality_assurance_status
|
||||||
|
|
||||||
- name: quality_assurance_distributed
|
|
||||||
commands: TIMEOUT=300 ./continuous_integration --distributed
|
|
||||||
infiles:
|
|
||||||
- . # current directory
|
|
||||||
- ../../build_release/memgraph_distributed # memgraph distributed release binary
|
|
||||||
outfile_paths: *OUTFILE_PATHS
|
|
||||||
|
|||||||
@@ -19,13 +19,6 @@
|
|||||||
commands: TIMEOUT=43200 ./continuous_integration --large-dataset
|
commands: TIMEOUT=43200 ./continuous_integration --large-dataset
|
||||||
infiles: *STRESS_INFILES
|
infiles: *STRESS_INFILES
|
||||||
|
|
||||||
- name: stress_ha
|
|
||||||
commands: TIMEOUT=200 ./continuous_integration_ha
|
|
||||||
infiles: &STRESS_HA_INFILES
|
|
||||||
- . # current directory
|
|
||||||
- ../../build_release/memgraph_ha # memgraph release binary
|
|
||||||
- ../../build_release/tests/stress/ # stress client binaries
|
|
||||||
|
|
||||||
- name: durability
|
- name: durability
|
||||||
commands: TIMEOUT=300 ./ve3/bin/python3 durability --num-steps 5
|
commands: TIMEOUT=300 ./ve3/bin/python3 durability --num-steps 5
|
||||||
infiles: &DURABILITY_INFILES
|
infiles: &DURABILITY_INFILES
|
||||||
|
|||||||
@@ -18,21 +18,9 @@ function(add_unit_test test_cpp)
|
|||||||
add_dependencies(memgraph__unit ${target_name})
|
add_dependencies(memgraph__unit ${target_name})
|
||||||
endfunction(add_unit_test)
|
endfunction(add_unit_test)
|
||||||
|
|
||||||
add_unit_test(ast_serialization.cpp)
|
|
||||||
target_link_libraries(${test_prefix}ast_serialization mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(bolt_encoder.cpp)
|
add_unit_test(bolt_encoder.cpp)
|
||||||
target_link_libraries(${test_prefix}bolt_encoder mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}bolt_encoder mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(cache.cpp)
|
|
||||||
target_link_libraries(${test_prefix}cache mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(commit_log_v2.cpp)
|
|
||||||
target_link_libraries(${test_prefix}commit_log_v2 glog gflags)
|
|
||||||
|
|
||||||
add_unit_test(concurrent_id_mapper_distributed.cpp)
|
|
||||||
target_link_libraries(${test_prefix}concurrent_id_mapper_distributed mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(concurrent_id_mapper_single_node.cpp)
|
add_unit_test(concurrent_id_mapper_single_node.cpp)
|
||||||
target_link_libraries(${test_prefix}concurrent_id_mapper_single_node mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}concurrent_id_mapper_single_node mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -45,18 +33,12 @@ target_link_libraries(${test_prefix}concurrent_map mg-single-node kvstore_dummy_
|
|||||||
add_unit_test(cypher_main_visitor.cpp)
|
add_unit_test(cypher_main_visitor.cpp)
|
||||||
target_link_libraries(${test_prefix}cypher_main_visitor mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}cypher_main_visitor mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(database_dump.cpp)
|
|
||||||
target_link_libraries(${test_prefix}database_dump mg-single-node kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(database_key_index.cpp)
|
add_unit_test(database_key_index.cpp)
|
||||||
target_link_libraries(${test_prefix}database_key_index mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}database_key_index mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(database_label_property_index.cpp)
|
add_unit_test(database_label_property_index.cpp)
|
||||||
target_link_libraries(${test_prefix}database_label_property_index mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}database_label_property_index mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(database_master.cpp)
|
|
||||||
target_link_libraries(${test_prefix}database_master mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(database_transaction_timeout.cpp)
|
add_unit_test(database_transaction_timeout.cpp)
|
||||||
target_link_libraries(${test_prefix}database_transaction_timeout mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}database_transaction_timeout mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -66,76 +48,22 @@ target_link_libraries(${test_prefix}datastructure_union_find mg-single-node kvst
|
|||||||
add_unit_test(deferred_deleter.cpp)
|
add_unit_test(deferred_deleter.cpp)
|
||||||
target_link_libraries(${test_prefix}deferred_deleter mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}deferred_deleter mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(distributed_coordination.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_coordination mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_data_exchange.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_data_exchange mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_dgp_vertex_migrator.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_dgp_vertex_migrator mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_durability.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_durability mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_dynamic_worker.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_dynamic_worker mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_edges_iterator.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_edges_iterator mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_gc.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_gc mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_graph_db.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_graph_db mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_interpretation.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_interpretation mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_plan_pretty_print.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_plan_pretty_print mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_query_plan.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_query_plan mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_reset.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_reset mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_serialization.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_serialization mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(distributed_updates.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_updates mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
# TODO (buda): Replace token sharing with centralized solution and write an appropriate test.
|
# TODO (buda): Replace token sharing with centralized solution and write an appropriate test.
|
||||||
# add_unit_test(distributed_token_sharing.cpp)
|
# add_unit_test(distributed_token_sharing.cpp)
|
||||||
# target_link_libraries(${test_prefix}distributed_token_sharing memgraph_lib kvstore_dummy_lib)
|
# target_link_libraries(${test_prefix}distributed_token_sharing memgraph_lib kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(bfs_distributed.cpp)
|
|
||||||
target_link_libraries(${test_prefix}bfs_distributed mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(bfs_single_node.cpp)
|
add_unit_test(bfs_single_node.cpp)
|
||||||
target_link_libraries(${test_prefix}bfs_single_node mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}bfs_single_node mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(distributed_dgp_partitioner.cpp)
|
|
||||||
target_link_libraries(${test_prefix}distributed_dgp_partitioner mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(durability.cpp)
|
add_unit_test(durability.cpp)
|
||||||
target_link_libraries(${test_prefix}durability mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}durability mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(dynamic_bitset.cpp)
|
add_unit_test(dynamic_bitset.cpp)
|
||||||
target_link_libraries(${test_prefix}dynamic_bitset mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}dynamic_bitset mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(edges_distributed.cpp)
|
|
||||||
target_link_libraries(${test_prefix}edges_distributed mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(edges_single_node.cpp)
|
add_unit_test(edges_single_node.cpp)
|
||||||
target_link_libraries(${test_prefix}edges_single_node mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}edges_single_node mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(gid.cpp)
|
|
||||||
target_link_libraries(${test_prefix}gid mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(graph_db_accessor.cpp)
|
add_unit_test(graph_db_accessor.cpp)
|
||||||
target_link_libraries(${test_prefix}graph_db_accessor mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}graph_db_accessor mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -151,9 +79,6 @@ target_link_libraries(${test_prefix}interpreter mg-single-node kvstore_dummy_lib
|
|||||||
add_unit_test(kvstore.cpp)
|
add_unit_test(kvstore.cpp)
|
||||||
target_link_libraries(${test_prefix}kvstore kvstore_lib glog)
|
target_link_libraries(${test_prefix}kvstore kvstore_lib glog)
|
||||||
|
|
||||||
add_unit_test(metrics.cpp)
|
|
||||||
target_link_libraries(${test_prefix}metrics mg-stats)
|
|
||||||
|
|
||||||
add_unit_test(mvcc.cpp)
|
add_unit_test(mvcc.cpp)
|
||||||
target_link_libraries(${test_prefix}mvcc mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}mvcc mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -175,9 +100,6 @@ target_link_libraries(${test_prefix}pod_buffer mg-single-node kvstore_dummy_lib)
|
|||||||
add_unit_test(property_value_store.cpp)
|
add_unit_test(property_value_store.cpp)
|
||||||
target_link_libraries(${test_prefix}property_value_store kvstore_lib mg-single-node)
|
target_link_libraries(${test_prefix}property_value_store kvstore_lib mg-single-node)
|
||||||
|
|
||||||
add_unit_test(replication_log.cpp)
|
|
||||||
target_link_libraries(${test_prefix}replication_log mg-single-node-ha kvstore_lib glog)
|
|
||||||
|
|
||||||
add_unit_test(query_cost_estimator.cpp)
|
add_unit_test(query_cost_estimator.cpp)
|
||||||
target_link_libraries(${test_prefix}query_cost_estimator mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}query_cost_estimator mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -211,9 +133,6 @@ target_link_libraries(${test_prefix}query_plan_match_filter_return mg-single-nod
|
|||||||
add_unit_test(query_plan.cpp)
|
add_unit_test(query_plan.cpp)
|
||||||
target_link_libraries(${test_prefix}query_plan mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}query_plan mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(query_required_privileges.cpp)
|
|
||||||
target_link_libraries(${test_prefix}query_required_privileges mg-single-node kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(query_semantic.cpp)
|
add_unit_test(query_semantic.cpp)
|
||||||
target_link_libraries(${test_prefix}query_semantic mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}query_semantic mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -244,15 +163,6 @@ target_link_libraries(${test_prefix}skiplist_reverse_iteration mg-single-node kv
|
|||||||
add_unit_test(skiplist_suffix.cpp)
|
add_unit_test(skiplist_suffix.cpp)
|
||||||
target_link_libraries(${test_prefix}skiplist_suffix mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}skiplist_suffix mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(slk_advanced.cpp)
|
|
||||||
target_link_libraries(${test_prefix}slk_advanced mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(slk_core.cpp)
|
|
||||||
target_link_libraries(${test_prefix}slk_core mg-slk glog gflags fmt)
|
|
||||||
|
|
||||||
add_unit_test(slk_streams.cpp)
|
|
||||||
target_link_libraries(${test_prefix}slk_streams mg-slk glog gflags fmt)
|
|
||||||
|
|
||||||
add_unit_test(small_vector.cpp)
|
add_unit_test(small_vector.cpp)
|
||||||
target_link_libraries(${test_prefix}small_vector mg-utils)
|
target_link_libraries(${test_prefix}small_vector mg-utils)
|
||||||
|
|
||||||
@@ -262,24 +172,15 @@ target_link_libraries(${test_prefix}state_delta mg-single-node kvstore_dummy_lib
|
|||||||
add_unit_test(static_bitset.cpp)
|
add_unit_test(static_bitset.cpp)
|
||||||
target_link_libraries(${test_prefix}static_bitset mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}static_bitset mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(storage_address.cpp)
|
|
||||||
target_link_libraries(${test_prefix}storage_address mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(storage_stat.cpp)
|
add_unit_test(storage_stat.cpp)
|
||||||
target_link_libraries(${test_prefix}storage_stat mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}storage_stat mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(stripped.cpp)
|
add_unit_test(stripped.cpp)
|
||||||
target_link_libraries(${test_prefix}stripped mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}stripped mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(transaction_engine_distributed.cpp)
|
|
||||||
target_link_libraries(${test_prefix}transaction_engine_distributed mg-distributed kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(transaction_engine_single_node.cpp)
|
add_unit_test(transaction_engine_single_node.cpp)
|
||||||
target_link_libraries(${test_prefix}transaction_engine_single_node mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}transaction_engine_single_node mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(transaction_engine_single_node_ha.cpp)
|
|
||||||
target_link_libraries(${test_prefix}transaction_engine_single_node_ha mg-single-node-ha kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(typed_value.cpp)
|
add_unit_test(typed_value.cpp)
|
||||||
target_link_libraries(${test_prefix}typed_value mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}typed_value mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
@@ -306,9 +207,6 @@ target_link_libraries(${test_prefix}communication_buffer mg-communication)
|
|||||||
add_unit_test(network_timeouts.cpp)
|
add_unit_test(network_timeouts.cpp)
|
||||||
target_link_libraries(${test_prefix}network_timeouts mg-communication)
|
target_link_libraries(${test_prefix}network_timeouts mg-communication)
|
||||||
|
|
||||||
add_unit_test(rpc.cpp)
|
|
||||||
target_link_libraries(${test_prefix}rpc mg-comm-rpc)
|
|
||||||
|
|
||||||
# Test data structures
|
# Test data structures
|
||||||
|
|
||||||
add_unit_test(ring_buffer.cpp)
|
add_unit_test(ring_buffer.cpp)
|
||||||
@@ -372,25 +270,6 @@ target_link_libraries(${test_prefix}utils_timestamp mg-utils)
|
|||||||
add_unit_test(utils_watchdog.cpp)
|
add_unit_test(utils_watchdog.cpp)
|
||||||
target_link_libraries(${test_prefix}utils_watchdog mg-utils)
|
target_link_libraries(${test_prefix}utils_watchdog mg-utils)
|
||||||
|
|
||||||
# Test mg-auth
|
|
||||||
|
|
||||||
add_unit_test(auth.cpp)
|
|
||||||
target_link_libraries(${test_prefix}auth mg-auth kvstore_lib)
|
|
||||||
|
|
||||||
# Test storage v2
|
|
||||||
|
|
||||||
add_unit_test(property_value_v2.cpp)
|
|
||||||
target_link_libraries(${test_prefix}property_value_v2 mg-utils)
|
|
||||||
|
|
||||||
add_unit_test(storage_v2_edge.cpp)
|
|
||||||
target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2)
|
|
||||||
|
|
||||||
add_unit_test(storage_v2.cpp)
|
|
||||||
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2)
|
|
||||||
|
|
||||||
add_unit_test(storage_v2_gc.cpp)
|
|
||||||
target_link_libraries(${test_prefix}storage_v2_gc mg-storage-v2)
|
|
||||||
|
|
||||||
# Test LCP
|
# Test LCP
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|||||||
@@ -50,11 +50,6 @@ class TestSession : public Session<TestInputStream, TestOutputStream> {
|
|||||||
|
|
||||||
void Abort() override {}
|
void Abort() override {}
|
||||||
|
|
||||||
bool Authenticate(const std::string &username,
|
|
||||||
const std::string &password) override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string query_;
|
std::string query_;
|
||||||
};
|
};
|
||||||
@@ -91,7 +86,7 @@ const uint8_t ignored_resp[] = {0x00, 0x02, 0xb0, 0x7e, 0x00, 0x00};
|
|||||||
|
|
||||||
// Write bolt chunk header (length)
|
// Write bolt chunk header (length)
|
||||||
void WriteChunkHeader(TestInputStream &input_stream, uint16_t len) {
|
void WriteChunkHeader(TestInputStream &input_stream, uint16_t len) {
|
||||||
len = utils::Bswap(len);
|
len = utils::HostToBigEndian(len);
|
||||||
input_stream.Write(reinterpret_cast<uint8_t *>(&len), sizeof(len));
|
input_stream.Write(reinterpret_cast<uint8_t *>(&len), sizeof(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2119,474 +2119,6 @@ TEST_P(CypherMainVisitorTest, UnionAll) {
|
|||||||
ASSERT_FALSE(return_clause->body_.distinct);
|
ASSERT_FALSE(return_clause->body_.distinct);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_auth_query(Base *ast_generator, std::string input,
|
|
||||||
AuthQuery::Action action, std::string user,
|
|
||||||
std::string role, std::string user_or_role,
|
|
||||||
std::optional<TypedValue> password,
|
|
||||||
std::vector<AuthQuery::Privilege> privileges) {
|
|
||||||
auto *auth_query =
|
|
||||||
dynamic_cast<AuthQuery *>(ast_generator->ParseQuery(input));
|
|
||||||
ASSERT_TRUE(auth_query);
|
|
||||||
EXPECT_EQ(auth_query->action_, action);
|
|
||||||
EXPECT_EQ(auth_query->user_, user);
|
|
||||||
EXPECT_EQ(auth_query->role_, role);
|
|
||||||
EXPECT_EQ(auth_query->user_or_role_, user_or_role);
|
|
||||||
ASSERT_EQ(static_cast<bool>(auth_query->password_),
|
|
||||||
static_cast<bool>(password));
|
|
||||||
if (password) {
|
|
||||||
ast_generator->CheckLiteral(auth_query->password_, *password);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(auth_query->privileges_, privileges);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, UserOrRoleName) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE ROLE `us|er`"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE ROLE `us er`"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "CREATE ROLE `user`",
|
|
||||||
AuthQuery::Action::CREATE_ROLE, "", "user", "", {}, {});
|
|
||||||
check_auth_query(&ast_generator, "CREATE ROLE us___er",
|
|
||||||
AuthQuery::Action::CREATE_ROLE, "", "us___er", "", {}, {});
|
|
||||||
check_auth_query(&ast_generator, "CREATE ROLE `us+er`",
|
|
||||||
AuthQuery::Action::CREATE_ROLE, "", "us+er", "", {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, CreateRole) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE ROLE"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "CREATE ROLE rola",
|
|
||||||
AuthQuery::Action::CREATE_ROLE, "", "rola", "", {}, {});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE ROLE lagano rolamo"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, DropRole) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DROP ROLE"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "DROP ROLE rola",
|
|
||||||
AuthQuery::Action::DROP_ROLE, "", "rola", "", {}, {});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DROP ROLE lagano rolamo"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowRoles) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW ROLES ROLES"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SHOW ROLES", AuthQuery::Action::SHOW_ROLES,
|
|
||||||
"", "", "", {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, CreateUser) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE USER"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE USER 123"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "CREATE USER user",
|
|
||||||
AuthQuery::Action::CREATE_USER, "user", "", "", {}, {});
|
|
||||||
check_auth_query(&ast_generator, "CREATE USER user IDENTIFIED BY 'password'",
|
|
||||||
AuthQuery::Action::CREATE_USER, "user", "", "",
|
|
||||||
TypedValue("password"), {});
|
|
||||||
check_auth_query(&ast_generator, "CREATE USER user IDENTIFIED BY ''",
|
|
||||||
AuthQuery::Action::CREATE_USER, "user", "", "",
|
|
||||||
TypedValue(""), {});
|
|
||||||
check_auth_query(&ast_generator, "CREATE USER user IDENTIFIED BY null",
|
|
||||||
AuthQuery::Action::CREATE_USER, "user", "", "", TypedValue(),
|
|
||||||
{});
|
|
||||||
ASSERT_THROW(
|
|
||||||
ast_generator.ParseQuery("CRATE USER user IDENTIFIED BY password"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE USER user IDENTIFIED BY 5"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CREATE USER user IDENTIFIED BY "),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, SetPassword) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET PASSWORD FOR"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET PASSWORD FOR user "),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SET PASSWORD FOR user TO null",
|
|
||||||
AuthQuery::Action::SET_PASSWORD, "user", "", "",
|
|
||||||
TypedValue(), {});
|
|
||||||
check_auth_query(&ast_generator, "SET PASSWORD FOR user TO 'password'",
|
|
||||||
AuthQuery::Action::SET_PASSWORD, "user", "", "",
|
|
||||||
TypedValue("password"), {});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET PASSWORD FOR user To 5"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, DropUser) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DROP USER"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "DROP USER user",
|
|
||||||
AuthQuery::Action::DROP_USER, "user", "", "", {}, {});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DROP USER lagano rolamo"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowUsers) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW USERS ROLES"), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SHOW USERS", AuthQuery::Action::SHOW_USERS,
|
|
||||||
"", "", "", {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, SetRole) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET ROLE"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET ROLE user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET ROLE FOR user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SET ROLE FOR user TO"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SET ROLE FOR user TO role",
|
|
||||||
AuthQuery::Action::SET_ROLE, "user", "role", "", {}, {});
|
|
||||||
check_auth_query(&ast_generator, "SET ROLE FOR user TO null",
|
|
||||||
AuthQuery::Action::SET_ROLE, "user", "null", "", {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ClearRole) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CLEAR ROLE"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CLEAR ROLE user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("CLEAR ROLE FOR user TO"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "CLEAR ROLE FOR user",
|
|
||||||
AuthQuery::Action::CLEAR_ROLE, "user", "", "", {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, GrantPrivilege) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("GRANT"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("GRANT TO user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("GRANT BLABLA TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("GRANT MATCH, TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("GRANT MATCH, BLABLA TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "GRANT MATCH TO user",
|
|
||||||
AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH});
|
|
||||||
check_auth_query(&ast_generator, "GRANT MATCH, AUTH TO user",
|
|
||||||
AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, DenyPrivilege) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DENY"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DENY TO user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DENY BLABLA TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DENY MATCH, TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("DENY MATCH, BLABLA TO user"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "DENY MATCH TO user",
|
|
||||||
AuthQuery::Action::DENY_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH});
|
|
||||||
check_auth_query(&ast_generator, "DENY MATCH, AUTH TO user",
|
|
||||||
AuthQuery::Action::DENY_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, RevokePrivilege) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("REVOKE"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("REVOKE FROM user"), SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("REVOKE BLABLA FROM user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("REVOKE MATCH, FROM user"),
|
|
||||||
SyntaxException);
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("REVOKE MATCH, BLABLA FROM user"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "REVOKE MATCH FROM user",
|
|
||||||
AuthQuery::Action::REVOKE_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH});
|
|
||||||
check_auth_query(&ast_generator, "REVOKE MATCH, AUTH FROM user",
|
|
||||||
AuthQuery::Action::REVOKE_PRIVILEGE, "", "", "user", {},
|
|
||||||
{AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
|
|
||||||
check_auth_query(&ast_generator, "REVOKE ALL PRIVILEGES FROM user",
|
|
||||||
AuthQuery::Action::REVOKE_PRIVILEGE, "", "", "user", {},
|
|
||||||
kPrivilegesAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowPrivileges) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW PRIVILEGES FOR"),
|
|
||||||
SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SHOW PRIVILEGES FOR user",
|
|
||||||
AuthQuery::Action::SHOW_PRIVILEGES, "", "", "user", {}, {});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW PRIVILEGES FOR user1, user2"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowRoleForUser) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW ROLE FOR "), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SHOW ROLE FOR user",
|
|
||||||
AuthQuery::Action::SHOW_ROLE_FOR_USER, "user", "", "", {},
|
|
||||||
{});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW ROLE FOR user1, user2"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowUsersForRole) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW USERS FOR "), SyntaxException);
|
|
||||||
check_auth_query(&ast_generator, "SHOW USERS FOR role",
|
|
||||||
AuthQuery::Action::SHOW_USERS_FOR_ROLE, "", "role", "", {},
|
|
||||||
{});
|
|
||||||
ASSERT_THROW(ast_generator.ParseQuery("SHOW USERS FOR role1, role2"),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, CreateStream) {
|
|
||||||
auto check_create_stream =
|
|
||||||
[this](std::string input, const std::string &stream_name,
|
|
||||||
const std::string &stream_uri, const std::string &stream_topic,
|
|
||||||
const std::string &transform_uri,
|
|
||||||
std::optional<int64_t> batch_interval_in_ms,
|
|
||||||
std::optional<int64_t> batch_size) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
EXPECT_EQ(stream_query->action_, StreamQuery::Action::CREATE_STREAM);
|
|
||||||
EXPECT_EQ(stream_query->stream_name_, stream_name);
|
|
||||||
ASSERT_TRUE(stream_query->stream_uri_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->stream_uri_,
|
|
||||||
TypedValue(stream_uri));
|
|
||||||
ASSERT_TRUE(stream_query->stream_topic_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->stream_topic_,
|
|
||||||
TypedValue(stream_topic));
|
|
||||||
ASSERT_TRUE(stream_query->transform_uri_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->transform_uri_,
|
|
||||||
TypedValue(transform_uri));
|
|
||||||
if (batch_interval_in_ms) {
|
|
||||||
ASSERT_TRUE(stream_query->batch_interval_in_ms_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->batch_interval_in_ms_,
|
|
||||||
TypedValue(*batch_interval_in_ms));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(stream_query->batch_interval_in_ms_, nullptr);
|
|
||||||
}
|
|
||||||
if (batch_size) {
|
|
||||||
ASSERT_TRUE(stream_query->batch_size_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->batch_size_,
|
|
||||||
TypedValue(*batch_size));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(stream_query->batch_size_, nullptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check_create_stream(
|
|
||||||
"CREATE STREAM stream AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TOPIC 'tropika' "
|
|
||||||
"WITH TRANSFORM 'localhost/test.py'",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", std::nullopt,
|
|
||||||
std::nullopt);
|
|
||||||
|
|
||||||
check_create_stream(
|
|
||||||
"CreaTE StreaM stream AS LOad daTA KAFKA 'localhost' "
|
|
||||||
"WitH TopIC 'tropika' "
|
|
||||||
"WITH TRAnsFORM 'localhost/test.py' bAtCH inTErvAL 168",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168, std::nullopt);
|
|
||||||
|
|
||||||
check_create_stream(
|
|
||||||
"CreaTE StreaM stream AS LOad daTA KAFKA 'localhost' "
|
|
||||||
"WITH TopIC 'tropika' "
|
|
||||||
"WITH TRAnsFORM 'localhost/test.py' bAtCH SizE 17",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", std::nullopt, 17);
|
|
||||||
|
|
||||||
check_create_stream(
|
|
||||||
"CreaTE StreaM stream AS LOad daTA KAFKA 'localhost' "
|
|
||||||
"WitH TOPic 'tropika' "
|
|
||||||
"WITH TRAnsFORM 'localhost/test.py' bAtCH inTErvAL 168 Batch SIze 17",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168, 17);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_create_stream(
|
|
||||||
"CREATE STREAM stream AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TRANSFORM 'localhost/test.py' BATCH INTERVAL 'jedan' ",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_create_stream(
|
|
||||||
"CREATE STREAM stream AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TOPIC 'tropika' "
|
|
||||||
"WITH TRANSFORM 'localhost/test.py' BATCH SIZE 'jedan' ",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py",
|
|
||||||
std::nullopt, 17),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_create_stream(
|
|
||||||
"CREATE STREAM 123 AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TOPIC 'tropika' "
|
|
||||||
"WITH TRANSFORM 'localhost/test.py' BATCH INTERVAL 168 ",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(
|
|
||||||
check_create_stream("CREATE STREAM stream AS LOAD DATA KAFKA localhost "
|
|
||||||
"WITH TOPIC 'tropika' "
|
|
||||||
"WITH TRANSFORM 'localhost/test.py'",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py",
|
|
||||||
std::nullopt, std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_create_stream(
|
|
||||||
"CREATE STREAM stream AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TOPIC 2"
|
|
||||||
"WITH TRANSFORM localhost/test.py BATCH INTERVAL 168 ",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_create_stream(
|
|
||||||
"CREATE STREAM stream AS LOAD DATA KAFKA 'localhost' "
|
|
||||||
"WITH TOPIC 'tropika'"
|
|
||||||
"WITH TRANSFORM localhost/test.py BATCH INTERVAL 168 ",
|
|
||||||
"stream", "localhost", "tropika", "localhost/test.py", 168,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, DropStream) {
|
|
||||||
auto check_drop_stream = [this](std::string input,
|
|
||||||
const std::string &stream_name) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
EXPECT_EQ(stream_query->action_, StreamQuery::Action::DROP_STREAM);
|
|
||||||
EXPECT_EQ(stream_query->stream_name_, stream_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
check_drop_stream("DRop stREAm stream", "stream");
|
|
||||||
check_drop_stream("DRop stREAm strim", "strim");
|
|
||||||
|
|
||||||
EXPECT_THROW(check_drop_stream("DROp sTREAM", ""), SyntaxException);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_drop_stream("DROP STreAM 123", "123"), SyntaxException);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_drop_stream("DroP STREAM '123'", "123"), SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, ShowStreams) {
|
|
||||||
auto check_show_streams = [this](std::string input) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
EXPECT_EQ(stream_query->action_, StreamQuery::Action::SHOW_STREAMS);
|
|
||||||
};
|
|
||||||
|
|
||||||
check_show_streams("SHOW STREAMS");
|
|
||||||
|
|
||||||
EXPECT_THROW(check_show_streams("SHOW STREAMS lololo"), SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, StartStopStream) {
|
|
||||||
auto check_start_stop_stream = [this](std::string input,
|
|
||||||
const std::string &stream_name,
|
|
||||||
bool is_start,
|
|
||||||
std::optional<int64_t> limit_batches) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
|
|
||||||
EXPECT_EQ(stream_query->stream_name_, stream_name);
|
|
||||||
EXPECT_EQ(stream_query->action_, is_start
|
|
||||||
? StreamQuery::Action::START_STREAM
|
|
||||||
: StreamQuery::Action::STOP_STREAM);
|
|
||||||
|
|
||||||
if (limit_batches) {
|
|
||||||
ASSERT_TRUE(is_start);
|
|
||||||
ASSERT_TRUE(stream_query->limit_batches_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->limit_batches_,
|
|
||||||
TypedValue(*limit_batches));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(stream_query->limit_batches_, nullptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check_start_stop_stream("stARt STreaM STREAM", "STREAM", true, std::nullopt);
|
|
||||||
check_start_stop_stream("stARt STreaM strim", "strim", true, std::nullopt);
|
|
||||||
check_start_stop_stream("StARt STreAM strim LimIT 10 BATchES", "strim", true,
|
|
||||||
10);
|
|
||||||
|
|
||||||
check_start_stop_stream("StoP StrEAM strim", "strim", false, std::nullopt);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_start_stop_stream("staRT STReaM 'strim'", "strim", true,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_start_stop_stream("sTART STReaM strim LImiT 'dva' BATCheS",
|
|
||||||
"strim", true, 2),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_start_stop_stream("StoP STreAM 'strim'", "strim", false,
|
|
||||||
std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_start_stop_stream("STOp sTREAM strim LIMit 2 baTCHES",
|
|
||||||
"strim", false, 2),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, StartStopAllStreams) {
|
|
||||||
auto check_start_stop_all_streams = [this](std::string input, bool is_start) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
EXPECT_EQ(stream_query->action_,
|
|
||||||
is_start ? StreamQuery::Action::START_ALL_STREAMS
|
|
||||||
: StreamQuery::Action::STOP_ALL_STREAMS);
|
|
||||||
};
|
|
||||||
|
|
||||||
check_start_stop_all_streams("STarT AlL StreAMs", true);
|
|
||||||
|
|
||||||
check_start_stop_all_streams("StoP aLL STrEAMs", false);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_start_stop_all_streams("StaRT aLL STreAM", true),
|
|
||||||
SyntaxException);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_start_stop_all_streams("SToP AlL STREaM", false),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, TestStream) {
|
|
||||||
auto check_test_stream = [this](std::string input,
|
|
||||||
const std::string &stream_name,
|
|
||||||
std::optional<int64_t> limit_batches) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *stream_query =
|
|
||||||
dynamic_cast<StreamQuery *>(ast_generator.ParseQuery(input));
|
|
||||||
ASSERT_TRUE(stream_query);
|
|
||||||
EXPECT_EQ(stream_query->stream_name_, stream_name);
|
|
||||||
EXPECT_EQ(stream_query->action_, StreamQuery::Action::TEST_STREAM);
|
|
||||||
|
|
||||||
if (limit_batches) {
|
|
||||||
ASSERT_TRUE(stream_query->limit_batches_);
|
|
||||||
ast_generator.CheckLiteral(stream_query->limit_batches_,
|
|
||||||
TypedValue(*limit_batches));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(stream_query->limit_batches_, nullptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check_test_stream("TesT STreaM strim", "strim", std::nullopt);
|
|
||||||
check_test_stream("TesT STreaM STREAM", "STREAM", std::nullopt);
|
|
||||||
check_test_stream("tESt STreAM STREAM LimIT 10 BATchES", "STREAM", 10);
|
|
||||||
|
|
||||||
check_test_stream("Test StrEAM STREAM", "STREAM", std::nullopt);
|
|
||||||
|
|
||||||
EXPECT_THROW(check_test_stream("tEST STReaM 'strim'", "strim", std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(
|
|
||||||
check_test_stream("test STReaM strim LImiT 'dva' BATCheS", "strim", 2),
|
|
||||||
SyntaxException);
|
|
||||||
EXPECT_THROW(check_test_stream("test STreAM 'strim'", "strim", std::nullopt),
|
|
||||||
SyntaxException);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CypherMainVisitorTest, TestExplainRegularQuery) {
|
TEST_P(CypherMainVisitorTest, TestExplainRegularQuery) {
|
||||||
auto &ast_generator = *GetParam();
|
auto &ast_generator = *GetParam();
|
||||||
EXPECT_TRUE(dynamic_cast<ExplainQuery *>(
|
EXPECT_TRUE(dynamic_cast<ExplainQuery *>(
|
||||||
@@ -2917,12 +2449,4 @@ TEST_P(CypherMainVisitorTest, RegexMatch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
||||||
TEST_P(CypherMainVisitorTest, DumpDatabase) {
|
|
||||||
auto &ast_generator = *GetParam();
|
|
||||||
auto *query =
|
|
||||||
dynamic_cast<DumpQuery *>(ast_generator.ParseQuery("DUMP DATABASE"));
|
|
||||||
ASSERT_TRUE(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -184,7 +184,8 @@ class DatabaseEnvironment {
|
|||||||
void Execute(GraphDbAccessor *dba, const std::string &query) {
|
void Execute(GraphDbAccessor *dba, const std::string &query) {
|
||||||
CHECK(dba);
|
CHECK(dba);
|
||||||
ResultStreamFaker<query::TypedValue> results;
|
ResultStreamFaker<query::TypedValue> results;
|
||||||
query::Interpreter()(query, *dba, {}, false).PullAll(results);
|
query::Interpreter()(query, *dba, {}, false, utils::NewDeleteResource())
|
||||||
|
.PullAll(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexAccessor CreateVertex(GraphDbAccessor *dba,
|
VertexAccessor CreateVertex(GraphDbAccessor *dba,
|
||||||
@@ -560,7 +561,8 @@ TEST(DumpTest, ExecuteDumpDatabase) {
|
|||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
const std::string query = "DUMP DATABASE";
|
const std::string query = "DUMP DATABASE";
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
auto results = query::Interpreter()(query, dba, {}, false);
|
auto results =
|
||||||
|
query::Interpreter()(query, dba, {}, false, utils::NewDeleteResource());
|
||||||
|
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ TEST(TransactionTimeout, TransactionTimeout) {
|
|||||||
query::Interpreter interpreter;
|
query::Interpreter interpreter;
|
||||||
auto interpret = [&](auto &dba, const std::string &query) {
|
auto interpret = [&](auto &dba, const std::string &query) {
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
interpreter(query, dba, {}, false).PullAll(stream);
|
interpreter(query, dba, {}, false, utils::NewDeleteResource())
|
||||||
|
.PullAll(stream);
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
auto dba = db.Access();
|
auto dba = db.Access();
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class InterpreterTest : public ::testing::Test {
|
|||||||
const std::map<std::string, PropertyValue> ¶ms = {}) {
|
const std::map<std::string, PropertyValue> ¶ms = {}) {
|
||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
auto results = interpreter_(query, dba, params, false);
|
auto results =
|
||||||
|
interpreter_(query, dba, params, false, utils::NewDeleteResource());
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
@@ -204,7 +205,7 @@ TEST_F(InterpreterTest, Bfs) {
|
|||||||
auto results = interpreter_(
|
auto results = interpreter_(
|
||||||
"MATCH (n {id: 0})-[r *bfs..5 (e, n | n.reachable and "
|
"MATCH (n {id: 0})-[r *bfs..5 (e, n | n.reachable and "
|
||||||
"e.reachable)]->(m) RETURN r",
|
"e.reachable)]->(m) RETURN r",
|
||||||
dba, {}, false);
|
dba, {}, false, utils::NewDeleteResource());
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
@@ -247,9 +248,10 @@ TEST_F(InterpreterTest, Bfs) {
|
|||||||
TEST_F(InterpreterTest, CreateIndexInMulticommandTransaction) {
|
TEST_F(InterpreterTest, CreateIndexInMulticommandTransaction) {
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
ASSERT_THROW(
|
ASSERT_THROW(interpreter_("CREATE INDEX ON :X(y)", dba, {}, true,
|
||||||
interpreter_("CREATE INDEX ON :X(y)", dba, {}, true).PullAll(stream),
|
utils::NewDeleteResource())
|
||||||
query::IndexInMulticommandTxException);
|
.PullAll(stream),
|
||||||
|
query::IndexInMulticommandTxException);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test shortest path end to end.
|
// Test shortest path end to end.
|
||||||
@@ -260,7 +262,7 @@ TEST_F(InterpreterTest, ShortestPath) {
|
|||||||
interpreter_(
|
interpreter_(
|
||||||
"CREATE (n:A {x: 1}), (m:B {x: 2}), (l:C {x: 1}), (n)-[:r1 {w: 1 "
|
"CREATE (n:A {x: 1}), (m:B {x: 2}), (l:C {x: 1}), (n)-[:r1 {w: 1 "
|
||||||
"}]->(m)-[:r2 {w: 2}]->(l), (n)-[:r3 {w: 4}]->(l)",
|
"}]->(m)-[:r2 {w: 2}]->(l), (n)-[:r3 {w: 4}]->(l)",
|
||||||
dba, {}, true)
|
dba, {}, true, utils::NewDeleteResource())
|
||||||
.PullAll(stream);
|
.PullAll(stream);
|
||||||
|
|
||||||
dba.Commit();
|
dba.Commit();
|
||||||
@@ -270,7 +272,7 @@ TEST_F(InterpreterTest, ShortestPath) {
|
|||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
auto results =
|
auto results =
|
||||||
interpreter_("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e",
|
interpreter_("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e",
|
||||||
dba, {}, false);
|
dba, {}, false, utils::NewDeleteResource());
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
@@ -308,44 +310,55 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
|
|||||||
{
|
{
|
||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
interpreter_("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;", dba,
|
interpreter_("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;", dba,
|
||||||
{}, true)
|
{}, true, utils::NewDeleteResource())
|
||||||
.PullAll(stream);
|
.PullAll(stream);
|
||||||
dba.Commit();
|
dba.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true).PullAll(stream);
|
interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true,
|
||||||
dba.Commit();
|
utils::NewDeleteResource())
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true).PullAll(stream);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
ASSERT_THROW(
|
|
||||||
interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true).PullAll(stream),
|
|
||||||
query::QueryRuntimeException);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
interpreter_("MATCH (n:A{a:2, b:2}) SET n.a=1", dba, {}, true)
|
|
||||||
.PullAll(stream);
|
.PullAll(stream);
|
||||||
interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true).PullAll(stream);
|
|
||||||
dba.Commit();
|
dba.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto dba = db_.Access();
|
auto dba = db_.Access();
|
||||||
interpreter_("MATCH (n:A{a:2, b:2}) DETACH DELETE n", dba, {}, true)
|
interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
|
.PullAll(stream);
|
||||||
|
dba.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto dba = db_.Access();
|
||||||
|
ASSERT_THROW(interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
|
.PullAll(stream),
|
||||||
|
query::QueryRuntimeException);
|
||||||
|
dba.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto dba = db_.Access();
|
||||||
|
interpreter_("MATCH (n:A{a:2, b:2}) SET n.a=1", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
|
.PullAll(stream);
|
||||||
|
interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
|
.PullAll(stream);
|
||||||
|
dba.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto dba = db_.Access();
|
||||||
|
interpreter_("MATCH (n:A{a:2, b:2}) DETACH DELETE n", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
|
.PullAll(stream);
|
||||||
|
interpreter_("CREATE (n:A{a:2, b:2})", dba, {}, true,
|
||||||
|
utils::NewDeleteResource())
|
||||||
.PullAll(stream);
|
.PullAll(stream);
|
||||||
interpreter_("CREATE (n:A{a:2, b:2})", dba, {}, true).PullAll(stream);
|
|
||||||
dba.Commit();
|
dba.Commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -671,7 +671,8 @@ TEST_F(PrintToJsonTest, Aggregate) {
|
|||||||
{PROPERTY_LOOKUP("node", value), nullptr, Aggregation::Op::SUM,
|
{PROPERTY_LOOKUP("node", value), nullptr, Aggregation::Op::SUM,
|
||||||
GetSymbol("sum")},
|
GetSymbol("sum")},
|
||||||
{PROPERTY_LOOKUP("node", value), PROPERTY_LOOKUP("node", color),
|
{PROPERTY_LOOKUP("node", value), PROPERTY_LOOKUP("node", color),
|
||||||
Aggregation::Op::COLLECT_MAP, GetSymbol("map")}},
|
Aggregation::Op::COLLECT_MAP, GetSymbol("map")},
|
||||||
|
{nullptr, nullptr, Aggregation::Op::COUNT, GetSymbol("count")}},
|
||||||
std::vector<Expression *>{PROPERTY_LOOKUP("node", type)},
|
std::vector<Expression *>{PROPERTY_LOOKUP("node", type)},
|
||||||
std::vector<Symbol>{node_sym});
|
std::vector<Symbol>{node_sym});
|
||||||
|
|
||||||
@@ -689,6 +690,10 @@ TEST_F(PrintToJsonTest, Aggregate) {
|
|||||||
"key" : "(PropertyLookup (Identifier \"node\") \"color\")",
|
"key" : "(PropertyLookup (Identifier \"node\") \"color\")",
|
||||||
"op" : "collect",
|
"op" : "collect",
|
||||||
"output_symbol" : "map"
|
"output_symbol" : "map"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "count",
|
||||||
|
"output_symbol": "count"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"group_by" : [
|
"group_by" : [
|
||||||
|
|||||||
@@ -1304,9 +1304,9 @@ TEST_F(FunctionTest, UniformSample) {
|
|||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull());
|
EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull());
|
||||||
ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull());
|
ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull());
|
||||||
ASSERT_THROW(
|
ASSERT_TRUE(
|
||||||
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()),
|
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue())
|
||||||
QueryRuntimeException);
|
.IsNull());
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull());
|
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull());
|
||||||
ASSERT_THROW(
|
ASSERT_THROW(
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ class QueryExecution : public testing::Test {
|
|||||||
* Does NOT commit the transaction */
|
* Does NOT commit the transaction */
|
||||||
auto Execute(const std::string &query) {
|
auto Execute(const std::string &query) {
|
||||||
ResultStreamFaker<query::TypedValue> stream;
|
ResultStreamFaker<query::TypedValue> stream;
|
||||||
auto results = query::Interpreter()(query, *dba_, {}, false);
|
auto results = query::Interpreter()(query, *dba_, {}, false,
|
||||||
|
utils::NewDeleteResource());
|
||||||
stream.Header(results.header());
|
stream.Header(results.header());
|
||||||
results.PullAll(stream);
|
results.PullAll(stream);
|
||||||
stream.Summary(results.summary());
|
stream.Summary(results.summary());
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ TEST(PoolResource, SingleSmallBlockAllocations) {
|
|||||||
EXPECT_EQ(test_mem.delete_count_, 0U);
|
EXPECT_EQ(test_mem.delete_count_, 0U);
|
||||||
mem.Release();
|
mem.Release();
|
||||||
EXPECT_GE(test_mem.delete_count_, 2U);
|
EXPECT_GE(test_mem.delete_count_, 2U);
|
||||||
|
CheckAllocation(&mem, 64U, 1U);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
@@ -241,6 +242,7 @@ TEST(PoolResource, MultipleSmallBlockAllocations) {
|
|||||||
EXPECT_TRUE(test_mem.new_count_ >= 3U && test_mem.new_count_ <= 6U);
|
EXPECT_TRUE(test_mem.new_count_ >= 3U && test_mem.new_count_ <= 6U);
|
||||||
mem.Release();
|
mem.Release();
|
||||||
EXPECT_GE(test_mem.delete_count_, 6U);
|
EXPECT_GE(test_mem.delete_count_, 6U);
|
||||||
|
CheckAllocation(&mem, 64U);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
@@ -260,6 +262,7 @@ TEST(PoolResource, BigBlockAllocations) {
|
|||||||
EXPECT_GE(test_mem.delete_count_, 1U);
|
EXPECT_GE(test_mem.delete_count_, 1U);
|
||||||
mem.Release();
|
mem.Release();
|
||||||
EXPECT_GE(test_mem.delete_count_, 3U);
|
EXPECT_GE(test_mem.delete_count_, 3U);
|
||||||
|
CheckAllocation(&mem, max_block_size + 1, 1U);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
@@ -303,6 +306,62 @@ TEST(PoolResource, AllocationWithOverflow) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PoolResource, BlockDeallocation) {
|
||||||
|
TestMemory test_mem;
|
||||||
|
const size_t max_blocks_per_chunk = 2U;
|
||||||
|
const size_t max_block_size = 64U;
|
||||||
|
utils::PoolResource mem(max_blocks_per_chunk, max_block_size, &test_mem);
|
||||||
|
auto *ptr = CheckAllocation(&mem, max_block_size);
|
||||||
|
test_mem.new_count_ = 0U;
|
||||||
|
// Do another allocation before deallocating `ptr`, so that we are sure that
|
||||||
|
// the chunk of 2 blocks is still alive and therefore `ptr` may be reused when
|
||||||
|
// it's deallocated. If we deallocate now, the implementation may choose to
|
||||||
|
// free the whole chunk, and we do not want that for the purposes of this
|
||||||
|
// test.
|
||||||
|
CheckAllocation(&mem, max_block_size);
|
||||||
|
EXPECT_EQ(test_mem.new_count_, 0U);
|
||||||
|
EXPECT_EQ(test_mem.delete_count_, 0U);
|
||||||
|
mem.Deallocate(ptr, max_block_size);
|
||||||
|
EXPECT_EQ(test_mem.delete_count_, 0U);
|
||||||
|
// CheckAllocation(&mem, max_block_size) will fail as PoolResource should
|
||||||
|
// reuse free blocks.
|
||||||
|
EXPECT_EQ(ptr, mem.Allocate(max_block_size));
|
||||||
|
EXPECT_EQ(test_mem.new_count_, 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AllocationTrackingMemory final : public utils::MemoryResource {
|
||||||
|
public:
|
||||||
|
std::vector<size_t> allocated_sizes_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *DoAllocate(size_t bytes, size_t alignment) override {
|
||||||
|
allocated_sizes_.push_back(bytes);
|
||||||
|
return utils::NewDeleteResource()->Allocate(bytes, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoDeallocate(void *ptr, size_t bytes, size_t alignment) override {
|
||||||
|
return utils::NewDeleteResource()->Deallocate(ptr, bytes, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoIsEqual(const utils::MemoryResource &other) const noexcept override {
|
||||||
|
return this == &other;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
|
TEST(MonotonicBufferResource, ResetGrowthFactor) {
|
||||||
|
AllocationTrackingMemory test_mem;
|
||||||
|
constexpr size_t stack_data_size = 1024;
|
||||||
|
char stack_data[stack_data_size];
|
||||||
|
utils::MonotonicBufferResource mem(&stack_data[0], stack_data_size,
|
||||||
|
&test_mem);
|
||||||
|
mem.Allocate(stack_data_size + 1);
|
||||||
|
mem.Release();
|
||||||
|
mem.Allocate(stack_data_size + 1);
|
||||||
|
ASSERT_EQ(test_mem.allocated_sizes_.size(), 2);
|
||||||
|
ASSERT_EQ(test_mem.allocated_sizes_.front(), test_mem.allocated_sizes_.back());
|
||||||
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
class ContainerWithAllocatorLast final {
|
class ContainerWithAllocatorLast final {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -4,22 +4,19 @@
|
|||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "utils/thread/sync.hpp"
|
#include "utils/rw_lock.hpp"
|
||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
using utils::RWLock;
|
|
||||||
using utils::RWLockPriority;
|
|
||||||
|
|
||||||
TEST(RWLock, MultipleReaders) {
|
TEST(RWLock, MultipleReaders) {
|
||||||
RWLock rwlock(RWLockPriority::READ);
|
utils::RWLock rwlock(utils::RWLock::Priority::READ);
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
utils::Timer timer;
|
utils::Timer timer;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
threads.push_back(std::thread([&rwlock] {
|
threads.push_back(std::thread([&rwlock] {
|
||||||
std::shared_lock<RWLock> lock(rwlock);
|
std::shared_lock<utils::RWLock> lock(rwlock);
|
||||||
std::this_thread::sleep_for(100ms);
|
std::this_thread::sleep_for(100ms);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -33,13 +30,13 @@ TEST(RWLock, MultipleReaders) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(RWLock, SingleWriter) {
|
TEST(RWLock, SingleWriter) {
|
||||||
RWLock rwlock(RWLockPriority::READ);
|
utils::RWLock rwlock(utils::RWLock::Priority::READ);
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
utils::Timer timer;
|
utils::Timer timer;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
threads.push_back(std::thread([&rwlock] {
|
threads.push_back(std::thread([&rwlock] {
|
||||||
std::unique_lock<RWLock> lock(rwlock);
|
std::unique_lock<utils::RWLock> lock(rwlock);
|
||||||
std::this_thread::sleep_for(100ms);
|
std::this_thread::sleep_for(100ms);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -59,19 +56,19 @@ TEST(RWLock, ReadPriority) {
|
|||||||
* - Thread 2 successfuly acquires a shared lock at T = 60ms, even though
|
* - Thread 2 successfuly acquires a shared lock at T = 60ms, even though
|
||||||
* there's a writer waiting.
|
* there's a writer waiting.
|
||||||
*/
|
*/
|
||||||
RWLock rwlock(RWLockPriority::READ);
|
utils::RWLock rwlock(utils::RWLock::Priority::READ);
|
||||||
rwlock.lock_shared();
|
rwlock.lock_shared();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
std::thread t1([&rwlock, &first] {
|
std::thread t1([&rwlock, &first] {
|
||||||
std::this_thread::sleep_for(30ms);
|
std::this_thread::sleep_for(30ms);
|
||||||
std::unique_lock<RWLock> lock(rwlock);
|
std::unique_lock<utils::RWLock> lock(rwlock);
|
||||||
EXPECT_FALSE(first);
|
EXPECT_FALSE(first);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread t2([&rwlock, &first] {
|
std::thread t2([&rwlock, &first] {
|
||||||
std::this_thread::sleep_for(60ms);
|
std::this_thread::sleep_for(60ms);
|
||||||
std::shared_lock<RWLock> lock(rwlock);
|
std::shared_lock<utils::RWLock> lock(rwlock);
|
||||||
EXPECT_TRUE(first);
|
EXPECT_TRUE(first);
|
||||||
first = false;
|
first = false;
|
||||||
});
|
});
|
||||||
@@ -89,20 +86,20 @@ TEST(RWLock, WritePriority) {
|
|||||||
* - Thread 2 tries to acquire a shared lock at T = 60ms, but it is not able
|
* - Thread 2 tries to acquire a shared lock at T = 60ms, but it is not able
|
||||||
* to because of write priority.
|
* to because of write priority.
|
||||||
*/
|
*/
|
||||||
RWLock rwlock(RWLockPriority::WRITE);
|
utils::RWLock rwlock(utils::RWLock::Priority::WRITE);
|
||||||
rwlock.lock_shared();
|
rwlock.lock_shared();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
std::thread t1([&rwlock, &first] {
|
std::thread t1([&rwlock, &first] {
|
||||||
std::this_thread::sleep_for(30ms);
|
std::this_thread::sleep_for(30ms);
|
||||||
std::unique_lock<RWLock> lock(rwlock);
|
std::unique_lock<utils::RWLock> lock(rwlock);
|
||||||
EXPECT_TRUE(first);
|
EXPECT_TRUE(first);
|
||||||
first = false;
|
first = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread t2([&rwlock, &first] {
|
std::thread t2([&rwlock, &first] {
|
||||||
std::this_thread::sleep_for(60ms);
|
std::this_thread::sleep_for(60ms);
|
||||||
std::shared_lock<RWLock> lock(rwlock);
|
std::shared_lock<utils::RWLock> lock(rwlock);
|
||||||
EXPECT_FALSE(first);
|
EXPECT_FALSE(first);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,7 +111,7 @@ TEST(RWLock, WritePriority) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(RWLock, TryLock) {
|
TEST(RWLock, TryLock) {
|
||||||
RWLock rwlock(RWLockPriority::WRITE);
|
utils::RWLock rwlock(utils::RWLock::Priority::WRITE);
|
||||||
rwlock.lock();
|
rwlock.lock();
|
||||||
|
|
||||||
std::thread t1([&rwlock] { EXPECT_FALSE(rwlock.try_lock()); });
|
std::thread t1([&rwlock] { EXPECT_FALSE(rwlock.try_lock()); });
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
add_executable(mg_import_csv mg_import_csv/main.cpp)
|
add_executable(mg_import_csv mg_import_csv/main.cpp)
|
||||||
target_link_libraries(mg_import_csv mg-single-node kvstore_dummy_lib)
|
target_link_libraries(mg_import_csv mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
# StatsD Target
|
|
||||||
add_executable(mg_statsd mg_statsd/main.cpp)
|
|
||||||
target_link_libraries(mg_statsd mg-communication mg-io mg-utils mg-stats)
|
|
||||||
|
|
||||||
# Generate a version.hpp file
|
# Generate a version.hpp file
|
||||||
set(VERSION_STRING ${memgraph_VERSION})
|
set(VERSION_STRING ${memgraph_VERSION})
|
||||||
configure_file(../../src/version.hpp.in version.hpp @ONLY)
|
configure_file(../../src/version.hpp.in version.hpp @ONLY)
|
||||||
@@ -19,11 +15,6 @@ if (READLINE_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
target_link_libraries(mg_client ${CLIENT_LIBS})
|
target_link_libraries(mg_client ${CLIENT_LIBS})
|
||||||
|
|
||||||
# Memgraph Dump Target
|
|
||||||
add_executable(mg_dump mg_dump/main.cpp)
|
|
||||||
target_include_directories(mg_dump PRIVATE ${MGCLIENT_INCLUDE_DIR})
|
|
||||||
target_link_libraries(mg_dump fmt gflags glog mgclient pthread)
|
|
||||||
|
|
||||||
# Strip the executable in release build.
|
# Strip the executable in release build.
|
||||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||||
if (lower_build_type STREQUAL "release")
|
if (lower_build_type STREQUAL "release")
|
||||||
@@ -39,4 +30,4 @@ install(TARGETS mg_import_csv RUNTIME DESTINATION bin)
|
|||||||
install(TARGETS mg_client RUNTIME DESTINATION bin)
|
install(TARGETS mg_client RUNTIME DESTINATION bin)
|
||||||
|
|
||||||
# Target for building all the tool executables.
|
# Target for building all the tool executables.
|
||||||
add_custom_target(tools DEPENDS mg_import_csv mg_statsd mg_client mg_dump)
|
add_custom_target(tools DEPENDS mg_import_csv mg_client)
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ include_directories(SYSTEM ${GTEST_INCLUDE_DIR})
|
|||||||
add_executable(mg_recovery_check mg_recovery_check.cpp)
|
add_executable(mg_recovery_check mg_recovery_check.cpp)
|
||||||
target_link_libraries(mg_recovery_check mg-single-node gtest gtest_main kvstore_dummy_lib)
|
target_link_libraries(mg_recovery_check mg-single-node gtest gtest_main kvstore_dummy_lib)
|
||||||
|
|
||||||
add_executable(mg_statsd_client statsd/mg_statsd_client.cpp)
|
|
||||||
target_link_libraries(mg_statsd_client mg-communication mg-io mg-utils mg-stats)
|
|
||||||
|
|
||||||
# Copy CSV data to CMake build dir
|
# Copy CSV data to CMake build dir
|
||||||
configure_file(csv/comment_nodes.csv csv/comment_nodes.csv COPYONLY)
|
configure_file(csv/comment_nodes.csv csv/comment_nodes.csv COPYONLY)
|
||||||
configure_file(csv/comment_nodes_2.csv csv/comment_nodes_2.csv COPYONLY)
|
configure_file(csv/comment_nodes_2.csv csv/comment_nodes_2.csv COPYONLY)
|
||||||
|
|||||||
Reference in New Issue
Block a user