Compare commits

...

25 Commits

Author SHA1 Message Date
Matej Ferencevic
974e0d388d Bump version and update changelog 2019-10-23 10:02:50 +02:00
Matej Ferencevic
fd36263b07 Reduce memory blowup on each Pull
Reviewers: teon.banek, buda

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2509
2019-10-23 10:01:39 +02:00
Matej Ferencevic
3d02f29bc3 Bump version and update changelog 2019-10-15 15:48:47 +02:00
Matej Ferencevic
7b7ce25608 Fix MonotonicBufferResource buffer growth
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2483
2019-10-15 15:41:27 +02:00
Lovro Lugovic
936dbbd34e An aggregate element's input expression can be NULL
Summary: For example, the aggregate element produced for `COUNT(*)` has its `value` set to `NULL`.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2463
2019-10-15 15:41:23 +02:00
Teon Banek
2acd5d80af Set -Werror=return-stack-address in CMakeLists
Summary:
This is a clang only flag, it promotes warnings to errors when returning
an address to something on the stack

Reviewers: mferencevic, ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2394
2019-10-15 15:41:19 +02:00
Teon Banek
702144bf09 Add missing include to scoped_profile.hpp
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2370
2019-10-15 15:41:16 +02:00
Teon Banek
50872cf893 Check for aggregations when planning RegexMatch
Reviewers: mferencevic, ipaljak, dsantl

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2344
2019-10-15 15:41:08 +02:00
Matej Ferencevic
6fad1cc43e Improve memory allocations in execution
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2340
2019-10-15 15:32:52 +02:00
Teon Banek
913bfb832b Correctly release PoolResource memory
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2341
2019-10-15 15:29:02 +02:00
Matej Ferencevic
851a0fcc36 Allocate initial execution memory
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2316
2019-10-15 15:28:54 +02:00
Teon Banek
84e5d70e2c Add PoolResource benchmarking in execution and skiplist
Summary:
With a pool allocator, lookups in STL set and map are up to 50% faster.
This is probably due to contiguous memory of pooled objects, i.e. nodes
of those containers. In some cases, the lookup outperforms the SkipList.
Insertions are also faster, though not as dramatically, up to 30%. This
does make a significant difference when the STL containers are used in a
single thread as they outperform the SkipList significantly.

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2326
2019-10-15 15:24:07 +02:00
Teon Banek
dd853ded31 Fix a bug in Pool::Deallocate
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2325
2019-10-15 15:24:03 +02:00
Teon Banek
7cd8e1c079 Prepend MonotonicBufferResource::Buffer to an allocation
Summary:
This is a different scheme for setting up a bookkeeping object while
still supporting arbitrary allocation alignment requests. The previous
scheme was simpler as it always allocated a power of 2 bytes, but the
trade-off was increased memory usage. This should waste less memory.

Reviewers: mtomic, mferencevic, ipaljak

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2321
2019-10-15 15:24:00 +02:00
Teon Banek
d6e4f1e3b6 Add utilities for type checking awesome MG functions
Reviewers: mtomic, msantl

Reviewed By: mtomic, msantl

Subscribers: buda, pullbot

Differential Revision: https://phabricator.memgraph.io/D2259
2019-10-15 15:23:53 +02:00
Teon Banek
d942934746 Update storage API docs with thrown exceptions
Summary:
The documentation includes `std` exceptions like `std::bad_alloc` or
`std::system_error`, for which there's probably nothing we can do. This
may seem unnecessary, but it will be really helpful when writing the C
API for interfacing with custom modules and plugins, as well as when
switching to storage v2 API.

In general, we should start updating the documentation of functions
which may throw exceptions. This ought to be enforced in code review, so
that the implementation and documentation are kept in sync.

Reviewers: mferencevic, mtomic, msantl

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2288
2019-10-15 15:22:09 +02:00
Matej Ferencevic
b5018b364b Replace bswap with proper endian functions
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2290
2019-10-15 15:22:03 +02:00
Matej Ferencevic
1cd26f40a2 Remove copy constructor/assignment from SkipList accessors
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2230
2019-10-15 15:21:51 +02:00
Teon Banek
e3f43f0212 Prepare utils/memory for switching to libstdc++-9
Reviewers: mtomic, mferencevic

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2246
2019-10-15 15:21:21 +02:00
Teon Banek
750098548f cmake: Allow absolute paths in define_add_lcp
Reviewers: mtomic, mferencevic

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2240
2019-10-15 15:21:17 +02:00
Teon Banek
b4435b801a Add non-template Value getters to PropertyValue
Reviewers: mtomic, mferencevic

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2216
2019-10-15 15:21:10 +02:00
Matej Ferencevic
90ab54c35b Cleanup locks
Summary: Move RWLock and replace exceptions with `CHECK`s

Reviewers: mtomic, teon.banek

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2205
2019-10-15 15:20:29 +02:00
Matej Ferencevic
82883d90b6 Remove leftover audit config files 2019-07-17 15:06:33 +02:00
Matej Ferencevic
91afe2910c Fix compilation on CentOS 2019-07-17 14:45:17 +02:00
Matej Ferencevic
4024fc96df Prepare release v0.15.0
Reviewers: teon.banek, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2211
2019-07-17 14:06:27 +02:00
96 changed files with 1377 additions and 4069 deletions

View File

@@ -1,5 +1,19 @@
# 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
### Breaking Changes

View File

@@ -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()
@@ -71,7 +71,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
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.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO

View File

@@ -1,12 +1,9 @@
- name: Binaries
archive:
- build_debug/memgraph
- build_debug/memgraph_distributed
- build_release/memgraph
- build_release/memgraph_distributed
- build_release/tools/src/mg_client
- build_release/tools/src/mg_import_csv
- build_release/tools/src/mg_statsd
- config
filename: binaries.tar.gz

View File

@@ -37,12 +37,8 @@
mkdir build_release
cd build_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
# Generate distributed card fraud dataset.
cd ../tests/distributed/card_fraud
./generate_dataset.sh
cd ../../..
TIMEOUT=1200 make -j$THREADS memgraph tools memgraph__macro_benchmark memgraph__stress
cd ..
# Checkout to parent commit and initialize.
cd ../parent
@@ -96,7 +92,3 @@
mkdir output
cd output
cpack -G DEB --config ../CPackConfig.cmake
# Generate distributed card fraud dataset.
cd ../../tests/distributed/card_fraud
./generate_dataset.sh

View File

@@ -205,22 +205,6 @@ import_external_library(rocksdb STATIC
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
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
import_external_library(libbcrypt STATIC
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/bcrypt.a
@@ -230,11 +214,3 @@ import_external_library(libbcrypt STATIC
CC=${CMAKE_C_COMPILER}
CXX=${CMAKE_CXX_COMPILER}
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})

View File

@@ -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
# remove shared library from install dependencies
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

View File

@@ -16,7 +16,7 @@ optdepends=()
provides=()
conflicts=()
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=()
install=memgraph.install
changelog=

View File

@@ -1,3 +1,2 @@
/etc/memgraph/memgraph.conf
/etc/logrotate.d/memgraph
/etc/logrotate.d/memgraph_audit

View File

@@ -120,7 +120,6 @@ chattr -i -R /usr/share/memgraph/examples || true
# uses plain %config.
%config(noreplace) "/etc/memgraph/memgraph.conf"
%config(noreplace) "/etc/logrotate.d/memgraph"
%config(noreplace) "/etc/logrotate.d/memgraph_audit"
@CPACK_RPM_USER_INSTALL_FILES@

View File

@@ -4,14 +4,9 @@
add_subdirectory(lisp)
add_subdirectory(utils)
add_subdirectory(requests)
add_subdirectory(integrations)
add_subdirectory(io)
add_subdirectory(telemetry)
add_subdirectory(communication)
add_subdirectory(stats)
add_subdirectory(auth)
add_subdirectory(slk)
add_subdirectory(storage/v2)
# ----------------------------------------------------------------------------
# Common LCP files
@@ -34,10 +29,8 @@ add_custom_target(generate_lcp_common DEPENDS ${generated_lcp_common_files})
# ----------------------------------------------------------------------------
set(mg_single_node_sources
${lcp_common_cpp_files}
audit/log.cpp
data_structures/concurrent/skiplist_gc.cpp
database/single_node/config.cpp
database/single_node/dump.cpp
database/single_node/graph_db.cpp
database/single_node/graph_db_accessor.cpp
durability/single_node/state_delta.cpp
@@ -45,13 +38,11 @@ set(mg_single_node_sources
durability/single_node/recovery.cpp
durability/single_node/snapshooter.cpp
durability/single_node/wal.cpp
glue/auth.cpp
glue/communication.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
@@ -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
antlr_opencypher_parser_lib dl glog gflags
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)
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
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# 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_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)
@@ -382,8 +155,6 @@ install(FILES ${CMAKE_SOURCE_DIR}/config/community.conf
# Install logrotate configuration (must use absolute path).
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate.conf
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.
install(CODE "file(MAKE_DIRECTORY \$ENV{DESTDIR}/var/log/memgraph
\$ENV{DESTDIR}/var/lib/memgraph)")
@@ -403,32 +174,3 @@ install(
${CMAKE_BINARY_DIR}/tests/manual/bolt_client
WORKING_DIRECTORY ${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)

View File

@@ -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 ${OPENSSL_LIBRARIES})
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)

View File

@@ -6,8 +6,8 @@
#include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/value.hpp"
#include "utils/bswap.hpp"
#include "utils/cast.hpp"
#include "utils/endian.hpp"
namespace communication::bolt {
@@ -186,18 +186,18 @@ class Decoder {
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
return false;
}
ret = utils::Bswap(tmp);
ret = utils::BigEndianToHost(tmp);
} else if (marker == Marker::Int32) {
int32_t tmp;
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
return false;
}
ret = utils::Bswap(tmp);
ret = utils::BigEndianToHost(tmp);
} else if (marker == Marker::Int64) {
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&ret), sizeof(ret))) {
return false;
}
ret = utils::Bswap(ret);
ret = utils::BigEndianToHost(ret);
} else {
return false;
}
@@ -212,7 +212,7 @@ class Decoder {
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&value), sizeof(value))) {
return false;
}
value = utils::Bswap(value);
value = utils::BigEndianToHost(value);
ret = utils::MemcpyCast<double>(value);
*data = Value(ret);
return true;
@@ -233,14 +233,14 @@ class Decoder {
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
return -1;
}
tmp = utils::Bswap(tmp);
tmp = utils::BigEndianToHost(tmp);
return tmp;
} else if (marker == Marker32[type]) {
uint32_t tmp;
if (!buffer_.Read(reinterpret_cast<uint8_t *>(&tmp), sizeof(tmp))) {
return -1;
}
tmp = utils::Bswap(tmp);
tmp = utils::BigEndianToHost(tmp);
return tmp;
} else {
return -1;

View File

@@ -4,8 +4,8 @@
#include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/value.hpp"
#include "utils/bswap.hpp"
#include "utils/cast.hpp"
#include "utils/endian.hpp"
static_assert(std::is_same_v<std::uint8_t, char> ||
std::is_same_v<std::uint8_t, unsigned char>,
@@ -216,7 +216,7 @@ class BaseEncoder {
private:
template <class T>
void WritePrimitiveValue(T value) {
value = utils::Bswap(value);
value = utils::HostToBigEndian(value);
WriteRAW(reinterpret_cast<const uint8_t *>(&value), sizeof(value));
}
};

View File

@@ -8,7 +8,6 @@
#include <glog/logging.h>
#include "communication/bolt/v1/constants.hpp"
#include "utils/bswap.hpp"
namespace communication::bolt {

View File

@@ -60,10 +60,6 @@ class Session {
/** Aborts currently running query. */
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.
* Goes through the bolt states in order to execute commands from the client.

View File

@@ -102,8 +102,8 @@ State HandleRun(TSession &session, State state, Marker marker) {
std::vector<Value> vec;
std::map<std::string, Value> data;
vec.reserve(header.size());
for (auto &i : header) vec.push_back(Value(i));
data.insert(std::make_pair(std::string("fields"), Value(vec)));
for (auto &i : header) vec.emplace_back(std::move(i));
data.emplace("fields", std::move(vec));
// Send the header.
if (!session.encoder_.MessageSuccess(data)) {
DLOG(WARNING) << "Couldn't send query header!";

View File

@@ -60,38 +60,6 @@ State StateInitRun(Session &session) {
LOG(INFO) << fmt::format("Client connected '{}'", client_name.ValueString())
<< 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.
if (!session.encoder_.MessageSuccess()) {
DLOG(WARNING) << "Couldn't send success message to the client!";

View File

@@ -91,6 +91,8 @@ class GraphDbAccessor {
* @param check_empty If the vertex should be checked for existing edges
* before deletion.
* @return If or not the vertex was deleted.
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
bool RemoveVertex(VertexAccessor &vertex_accessor, bool check_empty = true);
@@ -99,6 +101,9 @@ class GraphDbAccessor {
* and incoming connections.
*
* @param vertex_accessor Accessor to a vertex.
* @throw RecordDeletedError
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
void DetachRemoveVertex(VertexAccessor &vertex_accessor);
@@ -294,6 +299,9 @@ class GraphDbAccessor {
* recovering from durability.
*
* @return An accessor to the edge.
* @throw RecordDeletedError
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
EdgeAccessor InsertEdge(VertexAccessor &from, VertexAccessor &to,
storage::EdgeType type,
@@ -310,6 +318,9 @@ class GraphDbAccessor {
* side.
* @param remove_in_edge If the edge should be removed from the its
* destination side.
* @throw RecordDeletedError
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
void RemoveEdge(EdgeAccessor &edge, bool remove_out_edge = true,
bool remove_in_edge = true);
@@ -413,13 +424,14 @@ class GraphDbAccessor {
*
* @param label - label to build for
* @param property - property to build for
* @throw IndexExistsException
* @throw TransactionException
*/
void BuildIndex(storage::Label label, storage::Property property);
/// Deletes the index responisble for (label, property).
///
/// @throws IndexTransactionException if it can't obtain a blocking
/// transaction.
/// @throw TransactionException if it can't obtain a blocking transaction.
void DeleteIndex(storage::Label label, storage::Property property);
/// Populates index with vertices containing the key
@@ -433,10 +445,10 @@ class GraphDbAccessor {
* properties.
* 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.
* @throws TransactionEngineError if the engine doesn't accept transactions.
* @throws mvcc::SerializationError on serialization errors.
* @throw TransactionEngineError if the engine doesn't accept transactions.
* @throw mvcc::SerializationError on serialization errors.
*/
void BuildUniqueConstraint(storage::Label label,
const std::vector<storage::Property> &properties);
@@ -444,6 +456,7 @@ class GraphDbAccessor {
/**
* Deletes existing unique constraint.
* If the constraint doesn't exist, this method does nothing.
* @throw TransactionException
*/
void DeleteUniqueConstraint(storage::Label label,
const std::vector<storage::Property> &properties);
@@ -574,7 +587,10 @@ class GraphDbAccessor {
/** Returns the id of this accessor's transaction */
tx::TransactionId transaction_id() const;
/** Advances transaction's command id by 1. */
/**
* Advances transaction's command id by 1.
* @throw TransactionException
*/
void AdvanceCommand();
/** Commit transaction. */
@@ -622,6 +638,9 @@ class GraphDbAccessor {
* @param label - label that was added
* @param vertex_accessor - vertex_accessor that was updated
* @param vertex - vertex that was updated
* @throw utils::LockTimeoutException
* @throw SerializationError
* @throw ConstraintViolationException
*/
void UpdateOnAddLabel(storage::Label label,
const VertexAccessor &vertex_accessor,
@@ -632,6 +651,8 @@ class GraphDbAccessor {
*
* @param label - label that was removed
* @param vertex_accessor - vertex_accessor that was updated
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
void UpdateOnRemoveLabel(storage::Label label,
const RecordAccessor<Vertex> &accessor);
@@ -643,6 +664,8 @@ class GraphDbAccessor {
* @param previous_value - previous value of the property
* @param vertex_accessor - vertex_accessor that was updated
* @param vertex - vertex that was updated
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
void UpdateOnRemoveProperty(storage::Property property,
const PropertyValue &previous_value,
@@ -657,6 +680,9 @@ class GraphDbAccessor {
* @param new_value - new value of the property
* @param vertex_accessor - vertex accessor that was updated
* @param vertex - vertex that was updated
* @throw utils::LockTimeoutException
* @throw SerializationError
* @throw ConstraintViolationException
*/
void UpdateOnAddProperty(storage::Property property,
const PropertyValue &previous_value,

View File

@@ -3,7 +3,7 @@
#include <fstream>
#include "hasher.hpp"
#include "utils/bswap.hpp"
#include "utils/endian.hpp"
/**
* 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) {
if (!Read(reinterpret_cast<uint8_t *>(&val), sizeof(TValue), hash))
return false;
// TODO: must be platform specific in the future
val = utils::Bswap(val);
val = utils::BigEndianToHost(val);
return true;
}

View File

@@ -3,7 +3,7 @@
#include <fstream>
#include "hasher.hpp"
#include "utils/bswap.hpp"
#include "utils/endian.hpp"
/**
* Buffer that writes data to file and calculates hash of written data.
@@ -54,9 +54,8 @@ class HashedFileWriter {
*/
template <typename TValue>
void WriteValue(const TValue &val, bool hash = true) {
TValue val_bswapped = utils::Bswap(val);
Write(reinterpret_cast<const uint8_t *>(&val_bswapped), sizeof(TValue),
hash);
TValue val_big = utils::HostToBigEndian(val);
Write(reinterpret_cast<const uint8_t *>(&val_big), sizeof(TValue), hash);
}
// TODO try to remove before diff

View File

@@ -66,10 +66,6 @@ bool VersionConsistency(const fs::path &durability_dir) {
return true;
}
bool DistributedVersionConsistency(const int64_t master_version) {
return durability::kVersion == master_version;
}
bool ContainsDurabilityFiles(const fs::path &durability_dir) {
for (const auto &durability_type : {kSnapshotDir, kWalDir}) {
auto recovery_dir = durability_dir / durability_type;

View File

@@ -84,15 +84,6 @@ bool ReadSnapshotSummary(HashedFileReader &buffer, int64_t &vertex_count,
*/
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
* or write-ahead log file.

View File

@@ -26,13 +26,13 @@ query::TypedValue ToTypedValue(const Value &value) {
std::vector<query::TypedValue> list;
list.reserve(value.ValueList().size());
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: {
std::map<std::string, query::TypedValue> map;
for (const auto &kv : value.ValueMap())
map.emplace(kv.first, ToTypedValue(kv.second));
return query::TypedValue(map);
return query::TypedValue(std::move(map));
}
case Value::Type::Vertex:
case Value::Type::Edge:
@@ -61,14 +61,14 @@ Value ToBoltValue(const query::TypedValue &value) {
for (const auto &v : value.ValueList()) {
values.push_back(ToBoltValue(v));
}
return Value(values);
return Value(std::move(values));
}
case query::TypedValue::Type::Map: {
std::map<std::string, Value> map;
for (const auto &kv : value.ValueMap()) {
map.emplace(kv.first, ToBoltValue(kv.second));
}
return Value(map);
return Value(std::move(map));
}
case query::TypedValue::Type::Vertex:
return Value(ToBoltVertex(value.ValueVertex()));
@@ -91,7 +91,8 @@ communication::bolt::Vertex ToBoltVertex(const VertexAccessor &vertex) {
properties[vertex.db_accessor().PropertyName(prop.first)] =
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) {
@@ -104,7 +105,7 @@ communication::bolt::Edge ToBoltEdge(const EdgeAccessor &edge) {
properties[edge.db_accessor().PropertyName(prop.first)] =
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) {
@@ -175,7 +176,7 @@ Value ToBoltValue(const PropertyValue &value) {
for (const auto &v : values) {
vec.push_back(ToBoltValue(v));
}
return Value(vec);
return Value(std::move(vec));
}
case PropertyValue::Type::Map: {
const auto &map = value.Value<std::map<std::string, PropertyValue>>();
@@ -183,7 +184,7 @@ Value ToBoltValue(const PropertyValue &value) {
for (const auto &kv : map) {
dv_map.emplace(kv.first, ToBoltValue(kv.second));
}
return Value(dv_map);
return Value(std::move(dv_map));
}
}
}

View File

@@ -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:
# 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})
string(REGEX REPLACE "\.lcp$" ".hpp" h_file
"${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}")
if (IS_ABSOLUTE ${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)
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
set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE)
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/debug.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)
add_custom_command(OUTPUT ${h_file} ${cpp_file}
COMMAND ${CMAKE_SOURCE_DIR}/tools/lcp ${lcp_file} ${slk_serialize}

View File

@@ -9,11 +9,10 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "memgraph_init.hpp"
#include "communication/server.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 "telemetry/telemetry.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) "
"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 communication::ServerContext;
void SingleNodeMain() {
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);
// 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
database::GraphDb db;
query::Interpreter interpreter;
SessionData session_data{&db, &interpreter, &auth, &audit_log};
integrations::kafka::Streams kafka_streams{
durability_directory / "streams",
[&session_data](
const std::string &query,
const std::map<std::string, communication::bolt::Value> &params) {
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;
SessionData session_data{&db, &interpreter};
ServerContext context;
std::string service_name = "Bolt";

View File

@@ -3,7 +3,6 @@
#include <glog/logging.h>
#include "config.hpp"
#include "glue/auth.hpp"
#include "glue/communication.hpp"
#include "query/exceptions.hpp"
#include "requests/requests.hpp"
@@ -29,8 +28,6 @@ BoltSession::BoltSession(SessionData *data,
communication::OutputStream>(input_stream,
output_stream),
transaction_engine_(data->db, data->interpreter),
auth_(data->auth),
audit_log_(data->audit_log),
endpoint_(endpoint) {}
using TEncoder =
@@ -43,28 +40,8 @@ std::vector<std::string> BoltSession::Interpret(
std::map<std::string, PropertyValue> params_pv;
for (const auto &kv : params)
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 {
auto result = 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;
return transaction_engine_.Interpret(query, params_pv);
} catch (const query::QueryException &e) {
// Wrap QueryException into ClientError, because we want to allow the
// client to fix their query.
@@ -91,17 +68,6 @@ std::map<std::string, communication::bolt::Value> BoltSession::PullAll(
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)
: encoder_(encoder) {}
@@ -115,33 +81,6 @@ void BoltSession::TypedValueResultStream::Result(
encoder_->MessageRecord(decoded_values);
}
void KafkaStreamWriter(
SessionData &session_data, const std::string &query,
const std::map<std::string, communication::bolt::Value> &params) {
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.
// 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

View File

@@ -9,14 +9,13 @@
#include <gflags/gflags.h>
#include "audit/log.hpp"
#include "auth/auth.hpp"
#include "query/interpreter.hpp"
#include "query/transaction_engine.hpp"
#include "communication/bolt/v1/exceptions.hpp"
#include "communication/bolt/v1/session.hpp"
#include "communication/init.hpp"
#include "communication/session.hpp"
#include "query/interpreter.hpp"
#include "query/transaction_engine.hpp"
DECLARE_string(durability_directory);
@@ -25,16 +24,11 @@ DECLARE_string(durability_directory);
struct SessionData {
// Explicit constructor here to ensure that pointers to all objects are
// supplied.
SessionData(database::GraphDb *_db, query::Interpreter *_interpreter,
auth::Auth *_auth, audit::Log *_audit_log)
SessionData(database::GraphDb *_db, query::Interpreter *_interpreter)
: db(_db),
interpreter(_interpreter),
auth(_auth),
audit_log(_audit_log) {}
interpreter(_interpreter) {}
database::GraphDb *db;
query::Interpreter *interpreter;
auth::Auth *auth;
audit::Log *audit_log;
};
class BoltSession final
@@ -57,9 +51,6 @@ class BoltSession final
void Abort() override;
bool Authenticate(const std::string &username,
const std::string &password) override;
private:
/// Wrapper around TEncoder which converts TypedValue to Value
/// before forwarding the calls to original TEncoder.
@@ -74,26 +65,9 @@ class BoltSession final
};
query::TransactionEngine transaction_engine_;
auth::Auth *auth_;
std::optional<auth::User> user_;
audit::Log *audit_log_;
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> &params);
/// 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
/// startup function for `WithInit`, then you probably need to use this to

View File

@@ -7,8 +7,6 @@
namespace query {
static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
struct EvaluationContext {
/// Memory for allocations during evaluation of a *single* Pull call.
///

View File

@@ -122,19 +122,4 @@ class RemoveAttachedVertexException : public QueryRuntimeException {
"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

View File

@@ -2047,121 +2047,11 @@ cpp<#
(:serialize (:slk))
(: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)
((info-type "InfoType" :scope :public))
(:public
(lcp:define-enum info-type
(storage index constraint raft)
(storage index constraint)
(:serialize))
#>cpp

View File

@@ -61,11 +61,9 @@ class RemoveProperty;
class RemoveLabels;
class Merge;
class Unwind;
class AuthQuery;
class ExplainQuery;
class ProfileQuery;
class IndexQuery;
class StreamQuery;
class InfoQuery;
class ConstraintQuery;
class RegexMatch;
@@ -113,7 +111,7 @@ class ExpressionVisitor
template <class TResult>
class QueryVisitor
: public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery,
IndexQuery, AuthQuery, StreamQuery, InfoQuery,
IndexQuery, InfoQuery,
ConstraintQuery, DumpQuery> {};
} // namespace query

View File

@@ -61,9 +61,6 @@ antlrcpp::Any CypherMainVisitor::visitInfoQuery(
} else if (ctx->constraintInfo()) {
info_query->info_type_ = InfoQuery::InfoType::CONSTRAINT;
return info_query;
} else if (ctx->raftInfo()) {
info_query->info_type_ = InfoQuery::InfoType::RAFT;
return info_query;
} else {
throw utils::NotYetImplemented("Info query: '{}'", ctx->getText());
}
@@ -169,31 +166,6 @@ antlrcpp::Any CypherMainVisitor::visitDropIndex(
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(
MemgraphCypher::CypherUnionContext *ctx) {
bool distinct = !ctx->ALL();
@@ -346,382 +318,6 @@ antlrcpp::Any CypherMainVisitor::visitCreate(
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(
MemgraphCypher::CypherReturnContext *ctx) {
auto *return_clause = storage_->Create<Return>();

View File

@@ -176,22 +176,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
antlrcpp::Any visitConstraintQuery(
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*
*/
@@ -220,28 +204,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
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*
*/
@@ -253,136 +215,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
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*
*/

View File

@@ -46,9 +46,7 @@ indexInfo : INDEX INFO ;
constraintInfo : CONSTRAINT INFO ;
raftInfo : RAFT INFO ;
infoQuery : SHOW ( storageInfo | indexInfo | constraintInfo | raftInfo ) ;
infoQuery : SHOW ( storageInfo | indexInfo | constraintInfo ) ;
explainQuery : EXPLAIN cypherQuery ;

View File

@@ -115,7 +115,6 @@ OPTIONAL : O P T I O N A L ;
OR : O R ;
ORDER : O R D E R ;
PROFILE : P R O F I L E ;
RAFT : R A F T ;
REDUCE : R E D U C E ;
REMOVE : R E M O V E ;
RETURN : R E T U R N ;

View File

@@ -5,149 +5,3 @@ parser grammar MemgraphCypher ;
options { tokenVocab=MemgraphCypherLexer; }
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 ;

View File

@@ -9,39 +9,3 @@
lexer grammar MemgraphCypherLexer ;
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

View File

@@ -4,26 +4,15 @@
#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 "integrations/kafka/exceptions.hpp"
#include "integrations/kafka/streams.hpp"
#include "query/exceptions.hpp"
#include "query/frontend/ast/cypher_main_visitor.hpp"
#include "query/frontend/opencypher/parser.hpp"
#include "query/frontend/semantic/required_privileges.hpp"
#include "query/frontend/semantic/symbol_generator.hpp"
#include "query/interpret/eval.hpp"
#include "query/plan/planner.hpp"
#include "query/plan/profile.hpp"
#include "query/plan/vertex_count_cache.hpp"
#ifdef MG_SINGLE_NODE_HA
#include "raft/exceptions.hpp"
#endif
#include "utils/exceptions.hpp"
#include "utils/flag_validation.hpp"
#include "utils/string.hpp"
@@ -37,40 +26,6 @@ DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60,
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 {
public:
SingleNodeLogicalPlan(std::unique_ptr<plan::LogicalOperator> root,
@@ -118,466 +73,6 @@ TypedValue EvaluateOptionalExpression(Expression *expression,
return expression ? expression->Accept(*eval) : TypedValue();
}
Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
const Parameters &parameters,
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 &parameters,
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 &param : 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,
std::function<void()> invalidate_plan_cache,
database::GraphDbAccessor *db_accessor) {
@@ -631,7 +126,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
Callback callback;
switch (info_query->info_type_) {
case InfoQuery::InfoType::STORAGE:
#if defined(MG_SINGLE_NODE)
callback.header = {"storage info", "value"};
callback.fn = [db_accessor] {
auto info = db_accessor->StorageInfo();
@@ -642,24 +136,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
}
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;
case InfoQuery::InfoType::INDEX:
callback.header = {"created index"};
@@ -674,7 +150,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
};
break;
case InfoQuery::InfoType::CONSTRAINT:
#if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA)
callback.header = {"constraint type", "label", "properties"};
callback.fn = [db_accessor] {
std::vector<std::vector<TypedValue>> results;
@@ -693,27 +168,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
}
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;
}
return callback;
@@ -721,7 +175,6 @@ Callback HandleInfoQuery(InfoQuery *info_query,
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
database::GraphDbAccessor *db_accessor) {
#if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA)
std::vector<storage::Property> properties;
auto label = db_accessor->Label(constraint_query->constraint_.label.name);
properties.reserve(constraint_query->constraint_.properties.size());
@@ -773,9 +226,6 @@ Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
} break;
}
return callback;
#else
throw utils::NotYetImplemented("Constraints");
#endif
}
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
@@ -783,7 +233,7 @@ Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
Interpreter::Results Interpreter::operator()(
const std::string &query_string, database::GraphDbAccessor &db_accessor,
const std::map<std::string, PropertyValue> &params,
bool in_explicit_transaction) {
bool in_explicit_transaction, utils::MemoryResource *execution_memory) {
AstStorage ast_storage;
Parameters parameters;
std::map<std::string, TypedValue> summary;
@@ -809,17 +259,6 @@ Interpreter::Results Interpreter::operator()(
// we must ensure it lives during the whole interpretation.
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)) {
plan = CypherQueryToPlan(stripped_query.hash(), cypher_query,
std::move(ast_storage), parameters, &db_accessor);
@@ -839,8 +278,9 @@ Interpreter::Results Interpreter::operator()(
.first);
}
return Results(&db_accessor, parameters, plan, output_symbols, header,
summary, parsed_query.required_privileges);
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
std::move(header), std::move(summary),
execution_memory);
}
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()};
return Results(&db_accessor, parameters, plan, output_symbols, header,
summary, parsed_query.required_privileges);
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
std::move(header), std::move(summary),
execution_memory);
}
if (utils::IsSubtype(*parsed_query.query, ProfileQuery::kType)) {
@@ -990,37 +431,12 @@ Interpreter::Results Interpreter::operator()(
auto planning_time = planning_timer.Elapsed();
summary["planning_time"] = planning_time.count();
return Results(&db_accessor, parameters, plan, output_symbols, header,
summary, parsed_query.required_privileges,
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
std::move(header), std::move(summary),
execution_memory,
/* 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;
if (auto *index_query = utils::Downcast<IndexQuery>(parsed_query.query)) {
if (in_explicit_transaction) {
@@ -1035,30 +451,6 @@ Interpreter::Results Interpreter::operator()(
};
callback =
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 =
utils::Downcast<InfoQuery>(parsed_query.query)) {
callback = HandleInfoQuery(info_query, &db_accessor);
@@ -1085,8 +477,9 @@ Interpreter::Results Interpreter::operator()(
summary["planning_time"] = planning_time.count();
summary["cost_estimate"] = 0.0;
return Results(&db_accessor, parameters, plan, output_symbols,
callback.header, summary, parsed_query.required_privileges,
return Results(&db_accessor, parameters, plan, std::move(output_symbols),
callback.header, std::move(summary),
execution_memory,
/* is_profile_query */ false, callback.should_abort_query);
}
@@ -1123,8 +516,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
// Convert antlr4 AST into Memgraph AST.
frontend::CypherMainVisitor visitor(context, ast_storage);
visitor.visit(parser->tree());
return ParsedQuery{visitor.query(),
query::GetRequiredPrivileges(visitor.query())};
return ParsedQuery{visitor.query()};
}
auto stripped_query_hash = fnv(stripped_query);
@@ -1153,8 +545,7 @@ Interpreter::ParsedQuery Interpreter::ParseQuery(
AstStorage cached_ast_storage;
frontend::CypherMainVisitor visitor(context, &cached_ast_storage);
visitor.visit(parser->tree());
CachedQuery cached_query{std::move(cached_ast_storage), visitor.query(),
query::GetRequiredPrivileges(visitor.query())};
CachedQuery cached_query{std::move(cached_ast_storage), visitor.query()};
// Cache it.
ast_it =
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->labels_ = ast_it->second.ast_storage.labels_;
ast_storage->edge_types_ = ast_it->second.ast_storage.edge_types_;
return ParsedQuery{ast_it->second.query->Clone(ast_storage),
ast_it->second.required_privileges};
return ParsedQuery{ast_it->second.query->Clone(ast_storage)};
}
std::pair<frontend::StrippedQuery, Interpreter::ParsedQuery>

View File

@@ -17,14 +17,6 @@
DECLARE_bool(query_cost_planner);
DECLARE_int32(query_plan_cache_ttl);
namespace auth {
class Auth;
} // namespace auth
namespace integrations::kafka {
class Streams;
} // namespace integrations::kafka
namespace query {
// TODO: Maybe this should move to query/plan/planner.
@@ -63,19 +55,16 @@ class Interpreter {
struct CachedQuery {
AstStorage ast_storage;
Query *query;
std::vector<AuthQuery::Privilege> required_privileges;
};
using PlanCacheT = ConcurrentMap<HashType, std::shared_ptr<CachedPlan>>;
public:
/**
* Wraps a `Query` that was created as a result of parsing a query string
* along with its privileges.
* Wraps a `Query` that was created as a result of parsing a query string.
*/
struct ParsedQuery {
Query *query;
std::vector<AuthQuery::Privilege> required_privileges;
};
/**
@@ -89,18 +78,15 @@ class Interpreter {
std::shared_ptr<CachedPlan> plan,
std::vector<Symbol> output_symbols, std::vector<std::string> header,
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)
: ctx_{db_accessor},
plan_(plan),
execution_memory_(std::make_unique<utils::MonotonicBufferResource>(
kExecutionMemoryBlockSize)),
cursor_(plan_->plan().MakeCursor(execution_memory_.get())),
frame_(plan_->symbol_table().max_position(), execution_memory_.get()),
output_symbols_(output_symbols),
header_(header),
summary_(summary),
privileges_(std::move(privileges)),
cursor_(plan_->plan().MakeCursor(execution_memory)),
frame_(plan_->symbol_table().max_position(), execution_memory),
output_symbols_(std::move(output_symbols)),
header_(std::move(header)),
summary_(std::move(summary)),
should_abort_query_(should_abort_query) {
ctx_.is_profile_query = is_profile_query;
ctx_.symbol_table = plan_->symbol_table();
@@ -139,8 +125,10 @@ class Interpreter {
// enough for a single Pull.
constexpr size_t stack_size = 256 * 1024;
char stack_data[stack_size];
utils::MonotonicBufferResource memory(&stack_data[0], stack_size);
ctx_.evaluation_context.memory = &memory;
utils::MonotonicBufferResource monotonic_memory(&stack_data[0], stack_size);
// 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.
bool return_value = cursor_->Pull(frame_, ctx_);
if (return_value && !output_symbols_.empty()) {
@@ -175,11 +163,13 @@ class Interpreter {
while (Pull(stream)) continue;
}
const std::vector<std::string> &header() { return header_; }
const std::map<std::string, TypedValue> &summary() { return summary_; }
const std::vector<AuthQuery::Privilege> &privileges() {
return privileges_;
const std::vector<std::string> &header() const & { return header_; }
std::vector<std::string> &&header() && { return std::move(header_); }
const std::map<std::string, TypedValue> &summary() const & {
return summary_;
}
std::map<std::string, TypedValue> &&summary() && {
return std::move(summary_);
}
bool ShouldAbortQuery() const { return should_abort_query_; }
@@ -187,9 +177,6 @@ class Interpreter {
private:
ExecutionContext ctx_;
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_;
Frame frame_;
std::vector<Symbol> output_symbols_;
@@ -199,8 +186,6 @@ class Interpreter {
double execution_time_{0};
std::vector<AuthQuery::Privilege> privileges_;
bool should_abort_query_;
};
@@ -219,10 +204,8 @@ class Interpreter {
virtual Results operator()(const std::string &query,
database::GraphDbAccessor &db_accessor,
const std::map<std::string, PropertyValue> &params,
bool in_explicit_transaction);
auth::Auth *auth_ = nullptr;
integrations::kafka::Streams *kafka_streams_ = nullptr;
bool in_explicit_transaction,
utils::MemoryResource *execution_memory);
protected:
std::pair<frontend::StrippedQuery, ParsedQuery> StripAndParseQuery(

View File

@@ -360,7 +360,9 @@ json ToJson(const EdgeCreationInfo &edge_info,
json ToJson(const Aggregate::Element &elem) {
json json;
json["value"] = ToJson(elem.value);
if (elem.value) {
json["value"] = ToJson(elem.value);
}
if (elem.key) {
json["key"] = ToJson(elem.key);
}

View File

@@ -278,7 +278,7 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
#define VISIT_BINARY_OPERATOR(BinaryOperator) \
bool PostVisit(BinaryOperator &op) override { \
CHECK(has_aggregation_.size() >= 2U) \
CHECK(has_aggregation_.size() >= 2U) \
<< "Expected at least 2 has_aggregation_ flags."; \
/* has_aggregation_ stack is reversed, last result is from the 2nd */ \
/* expression. */ \
@@ -350,6 +350,15 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
return true;
}
bool PostVisit(RegexMatch &regex_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.
// This should be used when body.all_identifiers is true, to generate
// expressions for Produce operator.

View File

@@ -2,7 +2,9 @@
#include <cstdint>
#include "query/context.hpp"
#include "query/plan/profile.hpp"
#include "utils/likely.hpp"
#include "utils/tsc.hpp"
namespace query {

View File

@@ -63,20 +63,13 @@ void query::Repl(database::GraphDb *db, query::Interpreter *interpreter) {
try {
auto dba = db->Access();
ResultStreamFaker<query::TypedValue> stream;
#ifndef MG_DISTRIBUTED
auto results = (*interpreter)(command, dba, {}, false);
#else
auto results = (*interpreter)(command, *dba, {}, false);
#endif
auto results =
(*interpreter)(command, dba, {}, false, utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);
stream.Summary(results.summary());
std::cout << stream;
#ifndef MG_DISTRIBUTED
dba.Commit();
#else
dba->Commit();
#endif
} catch (const query::SyntaxException &e) {
std::cout << "SYNTAX EXCEPTION: " << e.what() << std::endl;
} catch (const query::LexingException &e) {

View File

@@ -5,22 +5,29 @@
#include "query/exceptions.hpp"
#include "query/interpreter.hpp"
#include "utils/likely.hpp"
#include "utils/memory.hpp"
#include "utils/string.hpp"
namespace query {
static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
class TransactionEngine final {
public:
TransactionEngine(database::GraphDb *db, Interpreter *interpreter)
: db_(db), interpreter_(interpreter) {}
: db_(db),
interpreter_(interpreter),
execution_memory_(&initial_memory_block_[0],
kExecutionMemoryBlockSize) {}
~TransactionEngine() { Abort(); }
std::pair<std::vector<std::string>, std::vector<query::AuthQuery::Privilege>>
std::vector<std::string>
Interpret(const std::string &query,
const std::map<std::string, PropertyValue> &params) {
// Clear pending results.
results_ = std::nullopt;
execution_memory_.Release();
// Check the query for transaction commands.
auto query_upper = utils::Trim(utils::ToUpperCase(query));
@@ -65,17 +72,18 @@ class TransactionEngine final {
if (in_explicit_transaction_ && db_accessor_) AdvanceCommand();
// Create a DB accessor if we don't yet have one.
#ifndef MG_DISTRIBUTED
if (!db_accessor_) db_accessor_.emplace(db_->Access());
#else
if (!db_accessor_) db_accessor_ = db_->Access();
#endif
// Clear leftover results.
results_ = std::nullopt;
execution_memory_.Release();
// Interpret the query and return the headers.
try {
results_.emplace((*interpreter_)(query, *db_accessor_, params,
in_explicit_transaction_));
return {results_->header(), results_->privileges()};
in_explicit_transaction_,
&execution_memory_));
return std::move(results_->header());
} catch (const utils::BasicException &) {
AbortCommand();
throw;
@@ -103,11 +111,6 @@ class TransactionEngine final {
}
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 &) {
AbortCommand();
throw;
@@ -116,25 +119,18 @@ class TransactionEngine final {
void Abort() {
results_ = std::nullopt;
execution_memory_.Release();
expect_rollback_ = false;
in_explicit_transaction_ = false;
if (!db_accessor_) return;
db_accessor_->Abort();
#ifndef MG_DISTRIBUTED
db_accessor_ = std::nullopt;
#else
db_accessor_ = nullptr;
#endif
}
private:
database::GraphDb *db_{nullptr};
Interpreter *interpreter_{nullptr};
#ifndef MG_DISTRIBUTED
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
// `database::GraphDbAccessor` is destroyed because the `Results` object holds
// references to the `GraphDb` object and will crash the database when
@@ -143,25 +139,27 @@ class TransactionEngine final {
bool in_explicit_transaction_{false};
bool expect_rollback_{false};
uint8_t initial_memory_block_[kExecutionMemoryBlockSize];
utils::MonotonicBufferResource execution_memory_;
void Commit() {
results_ = std::nullopt;
execution_memory_.Release();
if (!db_accessor_) return;
db_accessor_->Commit();
#ifndef MG_DISTRIBUTED
db_accessor_ = std::nullopt;
#else
db_accessor_ = nullptr;
#endif
}
void AdvanceCommand() {
results_ = std::nullopt;
execution_memory_.Release();
if (!db_accessor_) return;
db_accessor_->AdvanceCommand();
}
void AbortCommand() {
results_ = std::nullopt;
execution_memory_.Release();
if (in_explicit_transaction_) {
expect_rollback_ = true;
} else {

View File

@@ -14,6 +14,7 @@ class Transaction;
class RecordLock {
public:
/// @throw utils::LockTimeoutException
LockStatus Lock(const tx::Transaction &id, tx::Engine &engine);
void Unlock();

View File

@@ -7,6 +7,16 @@
#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
* info about the type.
@@ -92,6 +102,69 @@ class PropertyValue {
template <typename T>
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:
void DestroyValue();
@@ -113,16 +186,6 @@ class PropertyValue {
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
std::ostream &operator<<(std::ostream &os, const PropertyValue::Type type);
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);

View File

@@ -191,6 +191,8 @@ class VersionList {
* older visible record when this update is called.
*
* @param t The transaction
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
T *update(const tx::Transaction &t) {
DCHECK(head_ != nullptr) << "Head is nullptr on update.";
@@ -208,7 +210,11 @@ class VersionList {
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) {
DCHECK(record != nullptr) << "Record is nullptr on removal.";
lock_and_validate(record, t);
@@ -220,6 +226,8 @@ class VersionList {
int64_t cypher_id() { return utils::MemcpyCast<int64_t>(gid_); }
private:
/// @throw utils::LockTimeoutException
/// @throw SerializationError
void lock_and_validate(T *record, const tx::Transaction &t) {
DCHECK(record != nullptr) << "Record is nullptr on lock and validation.";
@@ -234,6 +242,8 @@ class VersionList {
throw SerializationError();
}
/// @throw utils::LockTimeoutException
/// @throw SerializationError
T *update(T *record, const tx::Transaction &t) {
DCHECK(record != nullptr) << "Record is nullptr on update.";
lock_and_validate(record, t);

View File

@@ -54,13 +54,27 @@ class RecordAccessor {
/** Gets the property for the given key. */
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);
/** Erases the property for the given key. */
/**
* Erases the property for the given key.
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
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();
/** 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
* deleted in the current transaction+command.
*
* @throws RecordDeletedError
* @throw RecordDeletedError
* @throw utils::LockTimeoutException
* @throw SerializationError
*/
TRecord &update() const;

View File

@@ -58,11 +58,22 @@ class VertexAccessor final : public RecordAccessor<Vertex> {
/** Returns the number of incoming edges. */
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);
/** 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);
/** 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
* 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);
/** 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
* 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);
};

View File

@@ -52,12 +52,12 @@ const nlohmann::json GetSystemInfo() {
auto mem_data = utils::ReadLines("/proc/meminfo");
for (auto &row : mem_data) {
auto tmp = utils::Trim(row);
if (utils::StartsWith(row, "MemTotal")) {
auto split = utils::Split(row);
if (utils::StartsWith(tmp, "MemTotal")) {
auto split = utils::Split(tmp);
if (split.size() < 2) continue;
memory = std::stoull(split[1]);
} else if (utils::StartsWith(row, "SwapTotal")) {
auto split = utils::Split(row);
} else if (utils::StartsWith(tmp, "SwapTotal")) {
auto split = utils::Split(tmp);
if (split.size() < 2) continue;
swap = std::stoull(split[1]);
}

View File

@@ -20,6 +20,7 @@ class LockStore {
public:
LockHolder() = default;
/// @throw utils::LockTimeoutException
LockHolder(RecordLock *lock, const Transaction &tx, tx::Engine &engine)
: lock_(lock) {
DCHECK(lock != nullptr) << "Lock is nullptr.";
@@ -57,6 +58,7 @@ class LockStore {
};
public:
/// @throw utils::LockTimeoutException
void Take(RecordLock *lock, const tx::Transaction &tx, tx::Engine &engine) {
// 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

View File

@@ -29,12 +29,15 @@ class Engine final {
Engine &operator=(const Engine &) = delete;
Engine &operator=(Engine &&) = delete;
/// @throw TransactionEngineError
Transaction *Begin();
/// 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
/// engine from creating new transactions and waits for the existing ones to
/// finish.
/// @throw TransactionEngineError
Transaction *BeginBlocking(std::optional<TransactionId> parent_tx);
/// @throw TransactionException
CommandId Advance(TransactionId id);
CommandId UpdateCommand(TransactionId id);
void Commit(const Transaction &t);

View File

@@ -54,6 +54,7 @@ class Transaction final {
public:
/// Acquires the lock over the given RecordLock, preventing other transactions
/// from doing the same
/// @throw utils::LockTimeoutException
void TakeLock(RecordLock &lock) const { locks_.Take(&lock, *this, engine_); }
/// Transaction's id. Unique in the engine that owns it
@@ -81,7 +82,8 @@ class Transaction final {
auto blocking() const { return blocking_; }
private:
// Function used to advance the command.
/// Function used to advance the command.
/// @throw TransactionError
CommandId AdvanceCommand() {
if (cid_ == std::numeric_limits<CommandId>::max()) {
throw TransactionError(

View File

@@ -4,7 +4,6 @@ set(utils_src_files
memory.cpp
signals.cpp
thread.cpp
thread/sync.cpp
uuid.cpp
watchdog.cpp)

View File

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

View File

@@ -37,10 +37,7 @@ MonotonicBufferResource::MonotonicBufferResource(void *buffer,
MemoryResource *memory)
: memory_(memory),
initial_buffer_(buffer),
initial_size_(buffer_size),
next_buffer_size_(GrowMonotonicBuffer(
initial_size_, std::numeric_limits<size_t>::max() - sizeof(Buffer))) {
}
initial_size_(buffer_size) {}
MonotonicBufferResource::MonotonicBufferResource(
MonotonicBufferResource &&other) noexcept
@@ -48,6 +45,7 @@ MonotonicBufferResource::MonotonicBufferResource(
current_buffer_(other.current_buffer_),
initial_buffer_(other.initial_buffer_),
initial_size_(other.initial_size_),
next_buffer_size_(other.next_buffer_size_),
allocated_(other.allocated_) {
other.current_buffer_ = nullptr;
}
@@ -60,6 +58,7 @@ MonotonicBufferResource &MonotonicBufferResource::operator=(
current_buffer_ = other.current_buffer_;
initial_buffer_ = other.initial_buffer_;
initial_size_ = other.initial_size_;
next_buffer_size_ = other.next_buffer_size_;
allocated_ = other.allocated_;
other.current_buffer_ = nullptr;
other.allocated_ = 0U;
@@ -69,14 +68,14 @@ MonotonicBufferResource &MonotonicBufferResource::operator=(
void MonotonicBufferResource::Release() {
for (auto *b = current_buffer_; b;) {
auto *next = b->next;
auto *ptr = b->data();
auto alloc_size = b->size();
auto alignment = b->alignment;
b->~Buffer();
memory_->Deallocate(ptr, alloc_size, alignment);
memory_->Deallocate(b, alloc_size, alignment);
b = next;
}
current_buffer_ = nullptr;
next_buffer_size_ = initial_size_;
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) {
// Set size so that the bytes fit.
size_t size = next_size > bytes ? next_size : bytes;
// Handle the case when we need to align `Buffer::data` to a greater
// `alignment`. We will simply always allocate Ceil2(required_size), so that
// we can use the end of the allocated bytes for Buffer instance.
// Simplify alignment by always using values greater or equal to max_align
size_t alloc_align = std::max(alignment, alignof(std::max_align_t));
// 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)),
"Buffer should not be a packed struct in order to be placed "
"at the end of an allocation request");
size_t bytes = sizeof(Buffer) + size;
if (bytes < size) throw BadAlloc("Allocation size overflow");
size_t alloc_size = Ceil2(bytes);
if (alloc_size < bytes) throw BadAlloc("Allocation size overflow");
size_t alloc_align = std::max(alignment, alignof(std::max_align_t));
"at the start of an allocation request");
size_t bytes_for_buffer = std::max(alloc_align, sizeof(Buffer));
size_t multiple = bytes_for_buffer / alloc_align;
if (bytes_for_buffer % alloc_align != 0) ++multiple;
bytes_for_buffer = multiple * alloc_align;
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);
// Instantiate the Buffer at the end of the allocated block.
current_buffer_ =
new (reinterpret_cast<char *>(ptr) + alloc_size - sizeof(Buffer))
Buffer{current_buffer_, alloc_size - sizeof(Buffer), alloc_align};
// Instantiate the Buffer at the start of the allocated block.
current_buffer_ = new (ptr)
Buffer{current_buffer_, alloc_size - bytes_for_buffer, alloc_align};
allocated_ = 0;
};
@@ -212,10 +220,15 @@ void Pool::Deallocate(void *p) {
ptr < reinterpret_cast<uintptr_t>(chunk.data + data_size);
};
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
auto *block = reinterpret_cast<unsigned char *>(p);
*block = chunk->first_available_block_ix;
chunk->first_available_block_ix = (block - chunk->data) / block_size_;
chunk->blocks_available++;
};
if (is_in_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);
}
chunks_.clear();
last_alloc_chunk_ = nullptr;
last_dealloc_chunk_ = nullptr;
}
} // namespace impl
@@ -350,6 +365,8 @@ void PoolResource::Release() {
GetUpstreamResource()->Deallocate(big_block.data, big_block.bytes,
big_block.alignment);
unpooled_.clear();
last_alloc_pool_ = nullptr;
last_dealloc_pool_ = nullptr;
}
// PoolResource END

View File

@@ -15,7 +15,11 @@
// Although <memory_resource> is in C++17, gcc libstdc++ still needs to
// implement it fully. It should be available in the next major release
// version, i.e. gcc 9.x.
#if _GLIBCXX_RELEASE < 9
#include <experimental/memory_resource>
#else
#include <memory_resource>
#endif
#include "utils/math.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
class StdMemoryResource final : public MemoryResource {
public:
/// Implicitly convert std::pmr::memory_resource to StdMemoryResource
#if _GLIBCXX_RELEASE < 9
StdMemoryResource(std::experimental::pmr::memory_resource *memory)
: memory_(memory) {}
#else
/// Implicitly convert std::pmr::memory_resource to StdMemoryResource
StdMemoryResource(std::pmr::memory_resource *memory) : memory_(memory) {}
#endif
private:
void *DoAllocate(size_t bytes, size_t alignment) override {
@@ -292,12 +300,20 @@ class StdMemoryResource final : public MemoryResource {
return *memory_ == *other_std->memory_;
}
#if _GLIBCXX_RELEASE < 9
std::experimental::pmr::memory_resource *memory_;
#else
std::pmr::memory_resource *memory_;
#endif
};
inline MemoryResource *NewDeleteResource() noexcept {
#if _GLIBCXX_RELEASE < 9
static StdMemoryResource memory(
std::experimental::pmr::new_delete_resource());
#else
static StdMemoryResource memory(std::pmr::new_delete_resource());
#endif
return &memory;
}
@@ -312,12 +328,8 @@ inline MemoryResource *NewDeleteResource() noexcept {
/// buffer is exhausted, a new one is requested from the upstream memory
/// resource.
///
/// Note that each buffer of memory is actually a block of `Ceil2(size +
/// sizeof(Buffer))` due to bookkeeping `Buffer` object being appended at the end.
/// 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.
/// Note that each buffer of memory is actually a larger block of at *least*
/// `(size + sizeof(Buffer))` bytes due to bookkeeping `Buffer` object.
class MonotonicBufferResource final : public MemoryResource {
public:
/// Construct the resource with the buffer size of at least `initial_size`.
@@ -361,10 +373,23 @@ class MonotonicBufferResource final : public MemoryResource {
Buffer *next;
size_t capacity;
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.
size_t size() const { return sizeof(*this) + capacity; }
/// Get the pointer to data which is before the Buffer instance itself.
char *data() { return reinterpret_cast<char *>(this) - capacity; }
size_t size() const { return bytes_for_buffer() + 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()};

124
src/utils/rw_lock.hpp Normal file
View 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

View File

@@ -575,16 +575,13 @@ class SkipList final {
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
}
Accessor(const Accessor &other)
: skiplist_(other.skiplist_), id_(skiplist_->gc_.AllocateId()) {}
Accessor(const Accessor &) = delete;
Accessor &operator=(const Accessor &) = delete;
Accessor(Accessor &&other) noexcept
: skiplist_(other.skiplist_), id_(other.id_) {
other.skiplist_ = nullptr;
}
Accessor &operator=(const Accessor &other) {
skiplist_ = other.skiplist_;
id_ = skiplist_->gc_.AllocateId();
}
Accessor &operator=(Accessor &&other) noexcept {
skiplist_ = other.skiplist_;
id_ = other.id_;
@@ -716,16 +713,13 @@ class SkipList final {
if (skiplist_ != nullptr) skiplist_->gc_.ReleaseId(id_);
}
ConstAccessor(const ConstAccessor &other)
: skiplist_(other.skiplist_), id_(skiplist_->gc_.AllocateId()) {}
ConstAccessor(const ConstAccessor &) = delete;
ConstAccessor &operator=(const ConstAccessor &) = delete;
ConstAccessor(ConstAccessor &&other) noexcept
: skiplist_(other.skiplist_), id_(other.id_) {
other.skiplist_ = nullptr;
}
ConstAccessor &operator=(const ConstAccessor &other) {
skiplist_ = other.skiplist_;
id_ = skiplist_->gc_.AllocateId();
}
ConstAccessor &operator=(ConstAccessor &&other) noexcept {
skiplist_ = other.skiplist_;
id_ = other.id_;

View File

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

View File

@@ -1,11 +1,6 @@
/// @file
#pragma once
#include <pthread.h>
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <mutex>
#include "utils/exceptions.hpp"
@@ -27,61 +22,6 @@ class LockTimeoutException : public 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.
///
/// It is implemented as a wrapper around std::lock_guard and std::unique_guard
@@ -104,78 +44,4 @@ class Lockable {
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

View File

@@ -23,6 +23,3 @@ add_subdirectory(property_based)
# integration test binaries
add_subdirectory(integration)
# feature benchmark test binaries
add_subdirectory(feature_benchmark)

View File

@@ -54,12 +54,6 @@ target_link_libraries(${test_prefix}edge_storage mg-single-node kvstore_dummy_li
add_benchmark(mvcc.cpp)
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)
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)
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)

View File

@@ -46,7 +46,8 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Match)(benchmark::State &state) {
auto dba = db_->Access();
while (state.KeepRunning()) {
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();
while (state.KeepRunning()) {
ResultStreamFaker<query::TypedValue> results;
interpreter()(query, dba, {}, false).PullAll(results);
interpreter()(query, dba, {}, false, utils::NewDeleteResource())
.PullAll(results);
}
}

View File

@@ -1,6 +1,7 @@
#include <benchmark/benchmark.h>
#include "query/interpret/eval.hpp"
#include "query/transaction_engine.hpp"
// The following classes are wrappers for utils::MemoryResource, so that we can
// use BENCHMARK_TEMPLATE

View File

@@ -7,10 +7,10 @@
#include "database/graph_db.hpp"
#include "database/graph_db_accessor.hpp"
#include "query/frontend/opencypher/parser.hpp"
#include "query/frontend/semantic/required_privileges.hpp"
#include "query/frontend/semantic/symbol_generator.hpp"
#include "query/interpreter.hpp"
#include "query/plan/planner.hpp"
#include "query/transaction_engine.hpp"
// The following classes are wrappers for utils::MemoryResource, so that we can
// use BENCHMARK_TEMPLATE
@@ -31,6 +31,15 @@ class NewDeleteResource final {
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) {
auto dba = db->Access();
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);
cypher_visitor.visit(parser.tree());
query::Interpreter::ParsedQuery parsed_query{
cypher_visitor.query(),
query::GetRequiredPrivileges(cypher_visitor.query())};
cypher_visitor.query()};
return utils::Downcast<query::CypherQuery>(parsed_query.query);
};
@@ -125,6 +133,10 @@ BENCHMARK_TEMPLATE(Distinct, MonotonicBufferResource)
->Range(1024, 1U << 21U)
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(Distinct, PoolResource)
->Range(1024, 1U << 21U)
->Unit(benchmark::kMicrosecond);
static query::plan::ExpandVariable MakeExpandVariable(
query::EdgeAtom::Type expand_type, query::SymbolTable *symbol_table) {
auto input_symbol = symbol_table->CreateSymbol("input", false);
@@ -178,6 +190,10 @@ BENCHMARK_TEMPLATE(ExpandVariable, MonotonicBufferResource)
->Ranges({{1, 1U << 5U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(ExpandVariable, PoolResource)
->Ranges({{1, 1U << 5U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void ExpandBfs(benchmark::State &state) {
@@ -214,6 +230,10 @@ BENCHMARK_TEMPLATE(ExpandBfs, MonotonicBufferResource)
->Range(512, 1U << 19U)
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(ExpandBfs, PoolResource)
->Range(512, 1U << 19U)
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void ExpandShortest(benchmark::State &state) {
@@ -255,6 +275,10 @@ BENCHMARK_TEMPLATE(ExpandShortest, MonotonicBufferResource)
->Range(512, 1U << 20U)
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(ExpandShortest, PoolResource)
->Range(512, 1U << 20U)
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void ExpandWeightedShortest(benchmark::State &state) {
@@ -300,6 +324,10 @@ BENCHMARK_TEMPLATE(ExpandWeightedShortest, MonotonicBufferResource)
->Range(512, 1U << 20U)
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(ExpandWeightedShortest, PoolResource)
->Range(512, 1U << 20U)
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void Accumulate(benchmark::State &state) {
@@ -340,6 +368,10 @@ BENCHMARK_TEMPLATE(Accumulate, MonotonicBufferResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(Accumulate, PoolResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void Aggregate(benchmark::State &state) {
@@ -393,6 +425,10 @@ BENCHMARK_TEMPLATE(Aggregate, MonotonicBufferResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(Aggregate, PoolResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void OrderBy(benchmark::State &state) {
@@ -439,6 +475,10 @@ BENCHMARK_TEMPLATE(OrderBy, MonotonicBufferResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(OrderBy, PoolResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
template <class TMemory>
// NOLINTNEXTLINE(google-runtime-references)
static void Unwind(benchmark::State &state) {
@@ -478,4 +518,8 @@ BENCHMARK_TEMPLATE(Unwind, MonotonicBufferResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_TEMPLATE(Unwind, PoolResource)
->Ranges({{4, 1U << 7U}, {512, 1U << 13U}})
->Unit(benchmark::kMicrosecond);
BENCHMARK_MAIN();

View File

@@ -5,6 +5,7 @@
#include <benchmark/benchmark.h>
#include "utils/memory.hpp"
#include "utils/skip_list.hpp"
#include "utils/spin_lock.hpp"
@@ -80,6 +81,42 @@ BENCHMARK_REGISTER_F(StdSetInsertFixture, Insert)
->Unit(benchmark::kNanosecond)
->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
///////////////////////////////////////////////////////////////////////////////
@@ -154,6 +191,44 @@ BENCHMARK_REGISTER_F(StdSetFindFixture, Find)
->Unit(benchmark::kNanosecond)
->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
///////////////////////////////////////////////////////////////////////////////
@@ -178,7 +253,7 @@ class SkipListMapInsertFixture : public benchmark::Fixture {
protected:
void SetUp(const benchmark::State &state) override {
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)
->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
///////////////////////////////////////////////////////////////////////////////
@@ -313,4 +425,43 @@ BENCHMARK_REGISTER_F(StdMapFindFixture, Find)
->Unit(benchmark::kNanosecond)
->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();

View File

@@ -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)
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)
target_link_libraries(${test_prefix}network_read_hang mg-single-node kvstore_dummy_lib)

View File

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

View File

@@ -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("[]")

View File

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

View File

@@ -6,36 +6,3 @@ add_subdirectory(ssl)
# transactions test binaries
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)

View File

@@ -22,122 +22,3 @@
- runner.sh # runner script
- ../../../build_debug/memgraph # memgraph 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

View File

@@ -15,16 +15,16 @@ function(add_macro_benchmark test_cpp)
endfunction(add_macro_benchmark)
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)
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)
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)
target_link_libraries(${test_prefix}query_client mg-communication mg-io mg-utils)
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)

View File

@@ -5,10 +5,7 @@
#include "gflags/gflags.h"
#include "communication/rpc/client.hpp"
#include "stats/stats.hpp"
#include "stats/stats_rpc_messages.hpp"
#include "utils/thread/sync.hpp"
#include "utils/rw_lock.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> max_tx_id;
utils::RWLock world_lock(utils::RWLockPriority::WRITE);
utils::RWLock world_lock(utils::RWLock::Priority::WRITE);
DEFINE_string(config, "", "test config");
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) {
std::string query = fmt::format("MATCH (u :{}) RETURN count(u)", label);
auto result = ExecuteNTimesTillSuccess(client, query, {}, MAX_RETRIES);
@@ -176,7 +165,6 @@ class CardFraudClient : public TestClient {
card_id, tx_id, pos_id);
num_transactions++;
UpdateStats();
}
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);
num_transactions = num_transactions_db;
UpdateStats();
}
std::this_thread::sleep_for(
@@ -334,9 +321,6 @@ int main(int argc, char **argv) {
communication::Init();
stats::InitStatsLogging(
fmt::format("client.long_running.{}.{}", FLAGS_group, FLAGS_scenario));
Endpoint endpoint(FLAGS_address, FLAGS_port);
ClientContext context(FLAGS_use_ssl);
Client client(&context);
@@ -382,7 +366,5 @@ int main(int argc, char **argv) {
RunMultithreadedTest(clients);
stats::StopStatsLogging();
return 0;
}

View File

@@ -6,8 +6,6 @@
#include "gflags/gflags.h"
#include "long_running_common.hpp"
#include "stats/stats.hpp"
#include "stats/stats_rpc_messages.hpp"
class Graph500BfsClient : public TestClient {
public:
@@ -55,7 +53,5 @@ int main(int argc, char **argv) {
RunMultithreadedTest(clients);
stats::StopStatsLogging();
return 0;
}

View File

@@ -14,8 +14,6 @@
#include "json/json.hpp"
#include "stats/metrics.hpp"
#include "stats/stats.hpp"
#include "utils/timer.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(scenario, "unknown", "Test scenario name");
auto &executed_queries = stats::GetCounter("executed_queries");
auto &executed_steps = stats::GetCounter("executed_steps");
auto &serialization_errors = stats::GetCounter("serialization_errors");
std::atomic<uint64_t> executed_queries;
std::atomic<uint64_t> executed_steps;
std::atomic<uint64_t> serialization_errors;
class TestClient {
public:
@@ -59,7 +57,7 @@ class TestClient {
runner_thread_ = std::thread([&] {
while (keep_running_) {
Step();
executed_steps.Bump();
++executed_steps;
}
});
}
@@ -82,7 +80,7 @@ class TestClient {
std::tie(result, retries) =
ExecuteNTimesTillSuccess(client_, query, params, MAX_RETRIES);
} catch (const utils::BasicException &e) {
serialization_errors.Bump(MAX_RETRIES);
serialization_errors += MAX_RETRIES;
return std::nullopt;
}
auto wall_time = timer.Elapsed();
@@ -96,8 +94,8 @@ class TestClient {
stats_[query].push_back(std::move(metadata));
}
}
executed_queries.Bump();
serialization_errors.Bump(retries);
++executed_queries;
serialization_errors += retries;
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;
it->second = (it->second.ValueDouble() * old_count + stat.second) /
(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() << ", "
<< "\"num_executed_steps\": " << executed_steps.Value() << ", "
out << "{\"num_executed_queries\": " << executed_queries << ", "
<< "\"num_executed_steps\": " << executed_steps << ", "
<< "\"elapsed_time\": " << timer.Elapsed().count()
<< ", \"queries\": [";
utils::PrintIterable(

View File

@@ -30,35 +30,6 @@ target_link_libraries(${test_prefix}binomial mg-utils)
add_manual_test(bolt_client.cpp)
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)
target_link_libraries(${test_prefix}kvstore_console kvstore_lib gflags glog)

View File

@@ -14,7 +14,8 @@ int main(int argc, char *argv[]) {
database::GraphDb db;
auto dba = db.Access();
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());
results.PullAll(stream);
stream.Summary(results.summary());

View File

@@ -5,10 +5,3 @@
- ../../build_release/memgraph # memgraph release binary
outfile_paths: &OUTFILE_PATHS
- \./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

View File

@@ -19,13 +19,6 @@
commands: TIMEOUT=43200 ./continuous_integration --large-dataset
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
commands: TIMEOUT=300 ./ve3/bin/python3 durability --num-steps 5
infiles: &DURABILITY_INFILES

View File

@@ -18,21 +18,9 @@ function(add_unit_test test_cpp)
add_dependencies(memgraph__unit ${target_name})
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)
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)
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)
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)
target_link_libraries(${test_prefix}database_key_index mg-single-node kvstore_dummy_lib)
add_unit_test(database_label_property_index.cpp)
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)
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)
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.
# add_unit_test(distributed_token_sharing.cpp)
# 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)
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)
target_link_libraries(${test_prefix}durability mg-single-node kvstore_dummy_lib)
add_unit_test(dynamic_bitset.cpp)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
target_link_libraries(${test_prefix}storage_stat mg-single-node kvstore_dummy_lib)
add_unit_test(stripped.cpp)
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)
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)
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)
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
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)
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
add_custom_command(

View File

@@ -50,11 +50,6 @@ class TestSession : public Session<TestInputStream, TestOutputStream> {
void Abort() override {}
bool Authenticate(const std::string &username,
const std::string &password) override {
return true;
}
private:
std::string query_;
};
@@ -91,7 +86,7 @@ const uint8_t ignored_resp[] = {0x00, 0x02, 0xb0, 0x7e, 0x00, 0x00};
// Write bolt chunk header (length)
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));
}

View File

@@ -2119,474 +2119,6 @@ TEST_P(CypherMainVisitorTest, UnionAll) {
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) {
auto &ast_generator = *GetParam();
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

View File

@@ -184,7 +184,8 @@ class DatabaseEnvironment {
void Execute(GraphDbAccessor *dba, const std::string &query) {
CHECK(dba);
ResultStreamFaker<query::TypedValue> results;
query::Interpreter()(query, *dba, {}, false).PullAll(results);
query::Interpreter()(query, *dba, {}, false, utils::NewDeleteResource())
.PullAll(results);
}
VertexAccessor CreateVertex(GraphDbAccessor *dba,
@@ -560,7 +561,8 @@ TEST(DumpTest, ExecuteDumpDatabase) {
auto dba = db.Access();
const std::string query = "DUMP DATABASE";
ResultStreamFaker<query::TypedValue> stream;
auto results = query::Interpreter()(query, dba, {}, false);
auto results =
query::Interpreter()(query, dba, {}, false, utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);

View File

@@ -13,7 +13,8 @@ TEST(TransactionTimeout, TransactionTimeout) {
query::Interpreter interpreter;
auto interpret = [&](auto &dba, const std::string &query) {
ResultStreamFaker<query::TypedValue> stream;
interpreter(query, dba, {}, false).PullAll(stream);
interpreter(query, dba, {}, false, utils::NewDeleteResource())
.PullAll(stream);
};
{
auto dba = db.Access();

View File

@@ -21,7 +21,8 @@ class InterpreterTest : public ::testing::Test {
const std::map<std::string, PropertyValue> &params = {}) {
auto dba = db_.Access();
ResultStreamFaker<query::TypedValue> stream;
auto results = interpreter_(query, dba, params, false);
auto results =
interpreter_(query, dba, params, false, utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);
stream.Summary(results.summary());
@@ -204,7 +205,7 @@ TEST_F(InterpreterTest, Bfs) {
auto results = interpreter_(
"MATCH (n {id: 0})-[r *bfs..5 (e, n | n.reachable and "
"e.reachable)]->(m) RETURN r",
dba, {}, false);
dba, {}, false, utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);
stream.Summary(results.summary());
@@ -247,9 +248,10 @@ TEST_F(InterpreterTest, Bfs) {
TEST_F(InterpreterTest, CreateIndexInMulticommandTransaction) {
ResultStreamFaker<query::TypedValue> stream;
auto dba = db_.Access();
ASSERT_THROW(
interpreter_("CREATE INDEX ON :X(y)", dba, {}, true).PullAll(stream),
query::IndexInMulticommandTxException);
ASSERT_THROW(interpreter_("CREATE INDEX ON :X(y)", dba, {}, true,
utils::NewDeleteResource())
.PullAll(stream),
query::IndexInMulticommandTxException);
}
// Test shortest path end to end.
@@ -260,7 +262,7 @@ TEST_F(InterpreterTest, ShortestPath) {
interpreter_(
"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)",
dba, {}, true)
dba, {}, true, utils::NewDeleteResource())
.PullAll(stream);
dba.Commit();
@@ -270,7 +272,7 @@ TEST_F(InterpreterTest, ShortestPath) {
auto dba = db_.Access();
auto results =
interpreter_("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e",
dba, {}, false);
dba, {}, false, utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);
stream.Summary(results.summary());
@@ -308,44 +310,55 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
{
auto dba = db_.Access();
interpreter_("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;", dba,
{}, true)
{}, true, utils::NewDeleteResource())
.PullAll(stream);
dba.Commit();
}
{
auto dba = db_.Access();
interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true).PullAll(stream);
dba.Commit();
}
{
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)
interpreter_("CREATE (:A{a:1, b:1})", dba, {}, true,
utils::NewDeleteResource())
.PullAll(stream);
interpreter_("CREATE (:A{a:2, b:2})", dba, {}, true).PullAll(stream);
dba.Commit();
}
{
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);
interpreter_("CREATE (n:A{a:2, b:2})", dba, {}, true).PullAll(stream);
dba.Commit();
}
}

View File

@@ -671,7 +671,8 @@ TEST_F(PrintToJsonTest, Aggregate) {
{PROPERTY_LOOKUP("node", value), nullptr, Aggregation::Op::SUM,
GetSymbol("sum")},
{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<Symbol>{node_sym});
@@ -689,6 +690,10 @@ TEST_F(PrintToJsonTest, Aggregate) {
"key" : "(PropertyLookup (Identifier \"node\") \"color\")",
"op" : "collect",
"output_symbol" : "map"
},
{
"op": "count",
"output_symbol": "count"
}
],
"group_by" : [

View File

@@ -1304,9 +1304,9 @@ TEST_F(FunctionTest, UniformSample) {
ASSERT_TRUE(
EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull());
ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull());
ASSERT_THROW(
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()),
QueryRuntimeException);
ASSERT_TRUE(
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue())
.IsNull());
ASSERT_TRUE(
EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull());
ASSERT_THROW(

View File

@@ -40,7 +40,8 @@ class QueryExecution : public testing::Test {
* Does NOT commit the transaction */
auto Execute(const std::string &query) {
ResultStreamFaker<query::TypedValue> stream;
auto results = query::Interpreter()(query, *dba_, {}, false);
auto results = query::Interpreter()(query, *dba_, {}, false,
utils::NewDeleteResource());
stream.Header(results.header());
results.PullAll(stream);
stream.Summary(results.summary());

View File

@@ -219,6 +219,7 @@ TEST(PoolResource, SingleSmallBlockAllocations) {
EXPECT_EQ(test_mem.delete_count_, 0U);
mem.Release();
EXPECT_GE(test_mem.delete_count_, 2U);
CheckAllocation(&mem, 64U, 1U);
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
@@ -241,6 +242,7 @@ TEST(PoolResource, MultipleSmallBlockAllocations) {
EXPECT_TRUE(test_mem.new_count_ >= 3U && test_mem.new_count_ <= 6U);
mem.Release();
EXPECT_GE(test_mem.delete_count_, 6U);
CheckAllocation(&mem, 64U);
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
@@ -260,6 +262,7 @@ TEST(PoolResource, BigBlockAllocations) {
EXPECT_GE(test_mem.delete_count_, 1U);
mem.Release();
EXPECT_GE(test_mem.delete_count_, 3U);
CheckAllocation(&mem, max_block_size + 1, 1U);
}
// 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)
class ContainerWithAllocatorLast final {
public:

View File

@@ -4,22 +4,19 @@
#include "glog/logging.h"
#include "gtest/gtest.h"
#include "utils/thread/sync.hpp"
#include "utils/rw_lock.hpp"
#include "utils/timer.hpp"
using namespace std::chrono_literals;
using utils::RWLock;
using utils::RWLockPriority;
TEST(RWLock, MultipleReaders) {
RWLock rwlock(RWLockPriority::READ);
utils::RWLock rwlock(utils::RWLock::Priority::READ);
std::vector<std::thread> threads;
utils::Timer timer;
for (int i = 0; i < 3; ++i) {
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);
}));
}
@@ -33,13 +30,13 @@ TEST(RWLock, MultipleReaders) {
}
TEST(RWLock, SingleWriter) {
RWLock rwlock(RWLockPriority::READ);
utils::RWLock rwlock(utils::RWLock::Priority::READ);
std::vector<std::thread> threads;
utils::Timer timer;
for (int i = 0; i < 3; ++i) {
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);
}));
}
@@ -59,19 +56,19 @@ TEST(RWLock, ReadPriority) {
* - Thread 2 successfuly acquires a shared lock at T = 60ms, even though
* there's a writer waiting.
*/
RWLock rwlock(RWLockPriority::READ);
utils::RWLock rwlock(utils::RWLock::Priority::READ);
rwlock.lock_shared();
bool first = true;
std::thread t1([&rwlock, &first] {
std::this_thread::sleep_for(30ms);
std::unique_lock<RWLock> lock(rwlock);
std::unique_lock<utils::RWLock> lock(rwlock);
EXPECT_FALSE(first);
});
std::thread t2([&rwlock, &first] {
std::this_thread::sleep_for(60ms);
std::shared_lock<RWLock> lock(rwlock);
std::shared_lock<utils::RWLock> lock(rwlock);
EXPECT_TRUE(first);
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
* to because of write priority.
*/
RWLock rwlock(RWLockPriority::WRITE);
utils::RWLock rwlock(utils::RWLock::Priority::WRITE);
rwlock.lock_shared();
bool first = true;
std::thread t1([&rwlock, &first] {
std::this_thread::sleep_for(30ms);
std::unique_lock<RWLock> lock(rwlock);
std::unique_lock<utils::RWLock> lock(rwlock);
EXPECT_TRUE(first);
first = false;
});
std::thread t2([&rwlock, &first] {
std::this_thread::sleep_for(60ms);
std::shared_lock<RWLock> lock(rwlock);
std::shared_lock<utils::RWLock> lock(rwlock);
EXPECT_FALSE(first);
});
@@ -114,7 +111,7 @@ TEST(RWLock, WritePriority) {
}
TEST(RWLock, TryLock) {
RWLock rwlock(RWLockPriority::WRITE);
utils::RWLock rwlock(utils::RWLock::Priority::WRITE);
rwlock.lock();
std::thread t1([&rwlock] { EXPECT_FALSE(rwlock.try_lock()); });

View File

@@ -2,10 +2,6 @@
add_executable(mg_import_csv mg_import_csv/main.cpp)
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
set(VERSION_STRING ${memgraph_VERSION})
configure_file(../../src/version.hpp.in version.hpp @ONLY)
@@ -19,11 +15,6 @@ if (READLINE_FOUND)
endif()
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.
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
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)
# 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)

View File

@@ -3,9 +3,6 @@ include_directories(SYSTEM ${GTEST_INCLUDE_DIR})
add_executable(mg_recovery_check mg_recovery_check.cpp)
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
configure_file(csv/comment_nodes.csv csv/comment_nodes.csv COPYONLY)
configure_file(csv/comment_nodes_2.csv csv/comment_nodes_2.csv COPYONLY)