Bring changes from master to project-pineapples (#477)
* Fix aggregation functions on `null` and group-by inputs (#448) * Upgrade Antrl to 4.10.1 and remove antlr_lock (#441) * Update clang-tidy job (#476) * Add parser stress test (#463) NOTE: Doing this to have buildable comments on the project-pineapples branch Co-authored-by: gvolfing <107616712+gvolfing@users.noreply.github.com> Co-authored-by: Jure Bajic <jure.bajic@memgraph.com>
This commit is contained in:
parent
2ceaf59767
commit
eb3f96d1f6
17
.github/workflows/diff.yaml
vendored
17
.github/workflows/diff.yaml
vendored
@ -70,6 +70,11 @@ jobs:
|
|||||||
# branches and tags. (default: 1)
|
# branches and tags. (default: 1)
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# This is also needed if we want do to comparison against other branches
|
||||||
|
# See https://github.community/t/checkout-code-fails-when-it-runs-lerna-run-test-since-master/17920
|
||||||
|
- name: Fetch all history for all tags and branches
|
||||||
|
run: git fetch
|
||||||
|
|
||||||
- name: Build combined ASAN, UBSAN and coverage binaries
|
- name: Build combined ASAN, UBSAN and coverage binaries
|
||||||
run: |
|
run: |
|
||||||
# Activate toolchain.
|
# Activate toolchain.
|
||||||
@ -110,12 +115,22 @@ jobs:
|
|||||||
name: "Code coverage"
|
name: "Code coverage"
|
||||||
path: tools/github/generated/code_coverage.tar.gz
|
path: tools/github/generated/code_coverage.tar.gz
|
||||||
|
|
||||||
|
- name: Set base branch
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
run: |
|
||||||
|
echo "BASE_BRANCH=origin/${{ github.base_ref }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set base branch # if we manually dispatch or push to master
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
run: |
|
||||||
|
echo "BASE_BRANCH=origin/master" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
source /opt/toolchain-v4/activate
|
source /opt/toolchain-v4/activate
|
||||||
|
|
||||||
# Restrict clang-tidy results only to the modified parts
|
# Restrict clang-tidy results only to the modified parts
|
||||||
git diff -U0 master... -- src | ./tools/github/clang-tidy/clang-tidy-diff.py -p 1 -j $THREADS -path build | tee ./build/clang_tidy_output.txt
|
git diff -U0 ${{ env.BASE_BRANCH }}... -- src | ./tools/github/clang-tidy/clang-tidy-diff.py -p 1 -j $THREADS -path build | tee ./build/clang_tidy_output.txt
|
||||||
|
|
||||||
# Fail if any warning is reported
|
# Fail if any warning is reported
|
||||||
! cat ./build/clang_tidy_output.txt | ./tools/github/clang-tidy/grep_error_lines.sh > /dev/null
|
! cat ./build/clang_tidy_output.txt | ./tools/github/clang-tidy/grep_error_lines.sh > /dev/null
|
||||||
|
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@ -5,3 +5,4 @@
|
|||||||
!CMakeLists.txt
|
!CMakeLists.txt
|
||||||
!__main.cpp
|
!__main.cpp
|
||||||
!pulsar.patch
|
!pulsar.patch
|
||||||
|
!antlr4.10.1.patch
|
||||||
|
@ -106,6 +106,7 @@ import_external_library(antlr4 STATIC
|
|||||||
-DWITH_LIBCXX=OFF # because of debian bug
|
-DWITH_LIBCXX=OFF # because of debian bug
|
||||||
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
||||||
-DCMAKE_CXX_STANDARD=20
|
-DCMAKE_CXX_STANDARD=20
|
||||||
|
-DANTLR_BUILD_CPP_TESTS=OFF
|
||||||
BUILD_COMMAND $(MAKE) antlr4_static
|
BUILD_COMMAND $(MAKE) antlr4_static
|
||||||
INSTALL_COMMAND $(MAKE) install)
|
INSTALL_COMMAND $(MAKE) install)
|
||||||
|
|
||||||
|
13
libs/antlr4.10.1.patch
Normal file
13
libs/antlr4.10.1.patch
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/runtime/Cpp/runtime/CMakeLists.txt b/runtime/Cpp/runtime/CMakeLists.txt
|
||||||
|
index baf46cac9..2e7756de8 100644
|
||||||
|
--- a/runtime/Cpp/runtime/CMakeLists.txt
|
||||||
|
+++ b/runtime/Cpp/runtime/CMakeLists.txt
|
||||||
|
@@ -134,7 +134,7 @@ set_target_properties(antlr4_static
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${LIB_OUTPUT_DIR}
|
||||||
|
COMPILE_FLAGS "${disabled_compile_warnings} ${extra_static_compile_flags}")
|
||||||
|
|
||||||
|
-install(TARGETS antlr4_shared
|
||||||
|
+install(TARGETS antlr4_shared OPTIONAL
|
||||||
|
EXPORT antlr4-targets
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
@ -1,43 +0,0 @@
|
|||||||
diff --git a/runtime/Cpp/runtime/CMakeLists.txt b/runtime/Cpp/runtime/CMakeLists.txt
|
|
||||||
index a8503bb..11362cf 100644
|
|
||||||
--- a/runtime/Cpp/runtime/CMakeLists.txt
|
|
||||||
+++ b/runtime/Cpp/runtime/CMakeLists.txt
|
|
||||||
@@ -5,8 +5,8 @@ set(THIRDPARTY_DIR ${CMAKE_BINARY_DIR}/runtime/thirdparty)
|
|
||||||
set(UTFCPP_DIR ${THIRDPARTY_DIR}/utfcpp)
|
|
||||||
ExternalProject_Add(
|
|
||||||
utfcpp
|
|
||||||
- GIT_REPOSITORY "git://github.com/nemtrif/utfcpp"
|
|
||||||
- GIT_TAG "v3.1.1"
|
|
||||||
+ GIT_REPOSITORY "https://github.com/nemtrif/utfcpp"
|
|
||||||
+ GIT_TAG "v3.2.1"
|
|
||||||
SOURCE_DIR ${UTFCPP_DIR}
|
|
||||||
UPDATE_DISCONNECTED 1
|
|
||||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${UTFCPP_DIR}/install -Dgtest_force_shared_crt=ON
|
|
||||||
@@ -118,7 +118,7 @@ set_target_properties(antlr4_static
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIB_OUTPUT_DIR}
|
|
||||||
COMPILE_FLAGS "${disabled_compile_warnings} ${extra_static_compile_flags}")
|
|
||||||
|
|
||||||
-install(TARGETS antlr4_shared
|
|
||||||
+install(TARGETS antlr4_shared OPTIONAL
|
|
||||||
DESTINATION lib
|
|
||||||
EXPORT antlr4-targets)
|
|
||||||
install(TARGETS antlr4_static
|
|
||||||
diff --git a/runtime/Cpp/runtime/src/support/Any.h b/runtime/Cpp/runtime/src/support/Any.h
|
|
||||||
index 468db98..65a473b 100644
|
|
||||||
--- a/runtime/Cpp/runtime/src/support/Any.h
|
|
||||||
+++ b/runtime/Cpp/runtime/src/support/Any.h
|
|
||||||
@@ -122,12 +122,12 @@ private:
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
- template<int N = 0, typename std::enable_if<N == N && std::is_nothrow_copy_constructible<T>::value, int>::type = 0>
|
|
||||||
+ template<int N = 0, typename std::enable_if<N == N && std::is_copy_constructible<T>::value, int>::type = 0>
|
|
||||||
Base* clone() const {
|
|
||||||
return new Derived<T>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
- template<int N = 0, typename std::enable_if<N == N && !std::is_nothrow_copy_constructible<T>::value, int>::type = 0>
|
|
||||||
+ template<int N = 0, typename std::enable_if<N == N && !std::is_copy_constructible<T>::value, int>::type = 0>
|
|
||||||
Base* clone() const {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
@ -105,7 +105,7 @@ repo_clone_try_double () {
|
|||||||
# Download from primary_urls might fail because the cache is not installed.
|
# Download from primary_urls might fail because the cache is not installed.
|
||||||
declare -A primary_urls=(
|
declare -A primary_urls=(
|
||||||
["antlr4-code"]="http://$local_cache_host/git/antlr4.git"
|
["antlr4-code"]="http://$local_cache_host/git/antlr4.git"
|
||||||
["antlr4-generator"]="http://$local_cache_host/file/antlr-4.9.2-complete.jar"
|
["antlr4-generator"]="http://$local_cache_host/file/antlr-4.10.1-complete.jar"
|
||||||
["cppitertools"]="http://$local_cache_host/git/cppitertools.git"
|
["cppitertools"]="http://$local_cache_host/git/cppitertools.git"
|
||||||
["rapidcheck"]="http://$local_cache_host/git/rapidcheck.git"
|
["rapidcheck"]="http://$local_cache_host/git/rapidcheck.git"
|
||||||
["gbenchmark"]="http://$local_cache_host/git/benchmark.git"
|
["gbenchmark"]="http://$local_cache_host/git/benchmark.git"
|
||||||
@ -130,7 +130,7 @@ declare -A primary_urls=(
|
|||||||
# should fail.
|
# should fail.
|
||||||
declare -A secondary_urls=(
|
declare -A secondary_urls=(
|
||||||
["antlr4-code"]="https://github.com/antlr/antlr4.git"
|
["antlr4-code"]="https://github.com/antlr/antlr4.git"
|
||||||
["antlr4-generator"]="http://www.antlr.org/download/antlr-4.9.2-complete.jar"
|
["antlr4-generator"]="https://www.antlr.org/download/antlr-4.10.1-complete.jar"
|
||||||
["cppitertools"]="https://github.com/ryanhaining/cppitertools.git"
|
["cppitertools"]="https://github.com/ryanhaining/cppitertools.git"
|
||||||
["rapidcheck"]="https://github.com/emil-e/rapidcheck.git"
|
["rapidcheck"]="https://github.com/emil-e/rapidcheck.git"
|
||||||
["gbenchmark"]="https://github.com/google/benchmark.git"
|
["gbenchmark"]="https://github.com/google/benchmark.git"
|
||||||
@ -152,10 +152,10 @@ declare -A secondary_urls=(
|
|||||||
# antlr
|
# antlr
|
||||||
file_get_try_double "${primary_urls[antlr4-generator]}" "${secondary_urls[antlr4-generator]}"
|
file_get_try_double "${primary_urls[antlr4-generator]}" "${secondary_urls[antlr4-generator]}"
|
||||||
|
|
||||||
antlr4_tag="4.9.2" # v4.9.2
|
antlr4_tag="4.10.1" # v4.10.1
|
||||||
repo_clone_try_double "${primary_urls[antlr4-code]}" "${secondary_urls[antlr4-code]}" "antlr4" "$antlr4_tag" true
|
repo_clone_try_double "${primary_urls[antlr4-code]}" "${secondary_urls[antlr4-code]}" "antlr4" "$antlr4_tag" true
|
||||||
pushd antlr4
|
pushd antlr4
|
||||||
git apply ../antlr4.patch
|
git apply ../antlr4.10.1.patch
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# cppitertools v2.0 2019-12-23
|
# cppitertools v2.0 2019-12-23
|
||||||
@ -199,7 +199,7 @@ git apply ../rocksdb.patch
|
|||||||
popd
|
popd
|
||||||
|
|
||||||
# mgclient
|
# mgclient
|
||||||
mgclient_tag="96e95c6845463cbe88948392be58d26da0d5ffd3" # (2022-02-08)
|
mgclient_tag="v1.4.0" # (2022-06-14)
|
||||||
repo_clone_try_double "${primary_urls[mgclient]}" "${secondary_urls[mgclient]}" "mgclient" "$mgclient_tag"
|
repo_clone_try_double "${primary_urls[mgclient]}" "${secondary_urls[mgclient]}" "mgclient" "$mgclient_tag"
|
||||||
sed -i 's/\${CMAKE_INSTALL_LIBDIR}/lib/' mgclient/src/CMakeLists.txt
|
sed -i 's/\${CMAKE_INSTALL_LIBDIR}/lib/' mgclient/src/CMakeLists.txt
|
||||||
|
|
||||||
|
@ -1252,9 +1252,8 @@ int main(int argc, char **argv) {
|
|||||||
// the triggers
|
// the triggers
|
||||||
auto storage_accessor = interpreter_context.db->Access();
|
auto storage_accessor = interpreter_context.db->Access();
|
||||||
auto dba = memgraph::query::DbAccessor{&storage_accessor};
|
auto dba = memgraph::query::DbAccessor{&storage_accessor};
|
||||||
interpreter_context.trigger_store.RestoreTriggers(&interpreter_context.ast_cache, &dba,
|
interpreter_context.trigger_store.RestoreTriggers(
|
||||||
&interpreter_context.antlr_lock, interpreter_context.config.query,
|
&interpreter_context.ast_cache, &dba, interpreter_context.config.query, interpreter_context.auth_checker);
|
||||||
interpreter_context.auth_checker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
||||||
|
@ -82,7 +82,7 @@ add_custom_command(
|
|||||||
OUTPUT ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include}
|
OUTPUT ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include}
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
||||||
COMMAND
|
COMMAND
|
||||||
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.9.2-complete.jar
|
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.10.1-complete.jar
|
||||||
-Dlanguage=Cpp -visitor -package antlropencypher
|
-Dlanguage=Cpp -visitor -package antlropencypher
|
||||||
-o ${opencypher_generated}
|
-o ${opencypher_generated}
|
||||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||||
|
@ -21,8 +21,7 @@ namespace memgraph::query {
|
|||||||
CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan) : plan_(std::move(plan)) {}
|
CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan) : plan_(std::move(plan)) {}
|
||||||
|
|
||||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
utils::SkipList<QueryCacheEntry> *cache, const InterpreterConfig::Query &query_config) {
|
||||||
const InterpreterConfig::Query &query_config) {
|
|
||||||
// Strip the query for caching purposes. The process of stripping a query
|
// Strip the query for caching purposes. The process of stripping a query
|
||||||
// "normalizes" it by replacing any literals with new parameters. This
|
// "normalizes" it by replacing any literals with new parameters. This
|
||||||
// results in just the *structure* of the query being taken into account for
|
// results in just the *structure* of the query being taken into account for
|
||||||
@ -63,9 +62,6 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (it == accessor.end()) {
|
if (it == accessor.end()) {
|
||||||
{
|
|
||||||
std::unique_lock<utils::SpinLock> guard(*antlr_lock);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parser = std::make_unique<frontend::opencypher::Parser>(stripped_query.query());
|
parser = std::make_unique<frontend::opencypher::Parser>(stripped_query.query());
|
||||||
} catch (const SyntaxException &e) {
|
} catch (const SyntaxException &e) {
|
||||||
@ -77,7 +73,6 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
|||||||
// up.
|
// up.
|
||||||
LOG_FATAL("The stripped query can't be parsed, but the original can.");
|
LOG_FATAL("The stripped query can't be parsed, but the original can.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the ANTLR4 parse tree into an AST.
|
// Convert the ANTLR4 parse tree into an AST.
|
||||||
AstStorage ast_storage;
|
AstStorage ast_storage;
|
||||||
|
@ -111,8 +111,7 @@ struct ParsedQuery {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
utils::SkipList<QueryCacheEntry> *cache, const InterpreterConfig::Query &query_config);
|
||||||
const InterpreterConfig::Query &query_config);
|
|
||||||
|
|
||||||
class SingleNodeLogicalPlan final : public LogicalPlan {
|
class SingleNodeLogicalPlan final : public LogicalPlan {
|
||||||
public:
|
public:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -115,7 +115,7 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
auto operators = ExtractOperators(all_children, allowed_operators);
|
auto operators = ExtractOperators(all_children, allowed_operators);
|
||||||
|
|
||||||
for (auto *expression : _expressions) {
|
for (auto *expression : _expressions) {
|
||||||
expressions.push_back(expression->accept(this));
|
expressions.push_back(std::any_cast<Expression *>(expression->accept(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression *first_operand = expressions[0];
|
Expression *first_operand = expressions[0];
|
||||||
@ -131,7 +131,7 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
DMG_ASSERT(_expression, "can't happen");
|
DMG_ASSERT(_expression, "can't happen");
|
||||||
auto operators = ExtractOperators(all_children, allowed_operators);
|
auto operators = ExtractOperators(all_children, allowed_operators);
|
||||||
|
|
||||||
Expression *expression = _expression->accept(this);
|
Expression *expression = std::any_cast<Expression *>(_expression->accept(this));
|
||||||
for (int i = (int)operators.size() - 1; i >= 0; --i) {
|
for (int i = (int)operators.size() - 1; i >= 0; --i) {
|
||||||
expression = CreateUnaryOperatorByToken(operators[i], expression);
|
expression = CreateUnaryOperatorByToken(operators[i], expression);
|
||||||
}
|
}
|
||||||
|
@ -1181,7 +1181,7 @@ PreparedQuery PrepareExplainQuery(ParsedQuery parsed_query, std::map<std::string
|
|||||||
// full query string) when given just the inner query to execute.
|
// full query string) when given just the inner query to execute.
|
||||||
ParsedQuery parsed_inner_query =
|
ParsedQuery parsed_inner_query =
|
||||||
ParseQuery(parsed_query.query_string.substr(kExplainQueryStart.size()), parsed_query.user_parameters,
|
ParseQuery(parsed_query.query_string.substr(kExplainQueryStart.size()), parsed_query.user_parameters,
|
||||||
&interpreter_context->ast_cache, &interpreter_context->antlr_lock, interpreter_context->config.query);
|
&interpreter_context->ast_cache, interpreter_context->config.query);
|
||||||
|
|
||||||
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
||||||
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in EXPLAIN");
|
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in EXPLAIN");
|
||||||
@ -1248,7 +1248,7 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
// full query string) when given just the inner query to execute.
|
// full query string) when given just the inner query to execute.
|
||||||
ParsedQuery parsed_inner_query =
|
ParsedQuery parsed_inner_query =
|
||||||
ParseQuery(parsed_query.query_string.substr(kProfileQueryStart.size()), parsed_query.user_parameters,
|
ParseQuery(parsed_query.query_string.substr(kProfileQueryStart.size()), parsed_query.user_parameters,
|
||||||
&interpreter_context->ast_cache, &interpreter_context->antlr_lock, interpreter_context->config.query);
|
&interpreter_context->ast_cache, interpreter_context->config.query);
|
||||||
|
|
||||||
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
||||||
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in PROFILE");
|
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in PROFILE");
|
||||||
@ -1566,8 +1566,7 @@ Callback CreateTrigger(TriggerQuery *trigger_query,
|
|||||||
interpreter_context->trigger_store.AddTrigger(
|
interpreter_context->trigger_store.AddTrigger(
|
||||||
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
|
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
|
||||||
dba, &interpreter_context->antlr_lock, interpreter_context->config.query, std::move(owner),
|
dba, interpreter_context->config.query, std::move(owner), interpreter_context->auth_checker);
|
||||||
interpreter_context->auth_checker);
|
|
||||||
return {};
|
return {};
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -2123,8 +2122,8 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
query_execution->summary["cost_estimate"] = 0.0;
|
query_execution->summary["cost_estimate"] = 0.0;
|
||||||
|
|
||||||
utils::Timer parsing_timer;
|
utils::Timer parsing_timer;
|
||||||
ParsedQuery parsed_query = ParseQuery(query_string, params, &interpreter_context_->ast_cache,
|
ParsedQuery parsed_query =
|
||||||
&interpreter_context_->antlr_lock, interpreter_context_->config.query);
|
ParseQuery(query_string, params, &interpreter_context_->ast_cache, interpreter_context_->config.query);
|
||||||
query_execution->summary["parsing_time"] = parsing_timer.Elapsed().count();
|
query_execution->summary["parsing_time"] = parsing_timer.Elapsed().count();
|
||||||
|
|
||||||
// Some queries require an active transaction in order to be prepared.
|
// Some queries require an active transaction in order to be prepared.
|
||||||
|
@ -173,13 +173,6 @@ struct InterpreterContext {
|
|||||||
|
|
||||||
storage::Storage *db;
|
storage::Storage *db;
|
||||||
|
|
||||||
// ANTLR has singleton instance that is shared between threads. It is
|
|
||||||
// protected by locks inside of ANTLR. Unfortunately, they are not protected
|
|
||||||
// in a very good way. Once we have ANTLR version without race conditions we
|
|
||||||
// can remove this lock. This will probably never happen since ANTLR
|
|
||||||
// developers introduce more bugs in each version. Fortunately, we have
|
|
||||||
// cache so this lock probably won't impact performance much...
|
|
||||||
utils::SpinLock antlr_lock;
|
|
||||||
std::optional<double> tsc_frequency{utils::GetTSCFrequency()};
|
std::optional<double> tsc_frequency{utils::GetTSCFrequency()};
|
||||||
std::atomic<bool> is_shutting_down{false};
|
std::atomic<bool> is_shutting_down{false};
|
||||||
|
|
||||||
|
@ -2600,13 +2600,13 @@ namespace {
|
|||||||
* when there are */
|
* when there are */
|
||||||
TypedValue DefaultAggregationOpValue(const Aggregate::Element &element, utils::MemoryResource *memory) {
|
TypedValue DefaultAggregationOpValue(const Aggregate::Element &element, utils::MemoryResource *memory) {
|
||||||
switch (element.op) {
|
switch (element.op) {
|
||||||
case Aggregation::Op::COUNT:
|
|
||||||
return TypedValue(0, memory);
|
|
||||||
case Aggregation::Op::SUM:
|
|
||||||
case Aggregation::Op::MIN:
|
case Aggregation::Op::MIN:
|
||||||
case Aggregation::Op::MAX:
|
case Aggregation::Op::MAX:
|
||||||
case Aggregation::Op::AVG:
|
case Aggregation::Op::AVG:
|
||||||
return TypedValue(memory);
|
return TypedValue(memory);
|
||||||
|
case Aggregation::Op::COUNT:
|
||||||
|
case Aggregation::Op::SUM:
|
||||||
|
return TypedValue(0, memory);
|
||||||
case Aggregation::Op::COLLECT_LIST:
|
case Aggregation::Op::COLLECT_LIST:
|
||||||
return TypedValue(TypedValue::TVector(memory));
|
return TypedValue(TypedValue::TVector(memory));
|
||||||
case Aggregation::Op::COLLECT_MAP:
|
case Aggregation::Op::COLLECT_MAP:
|
||||||
@ -2628,9 +2628,7 @@ class AggregateCursor : public Cursor {
|
|||||||
pulled_all_input_ = true;
|
pulled_all_input_ = true;
|
||||||
aggregation_it_ = aggregation_.begin();
|
aggregation_it_ = aggregation_.begin();
|
||||||
|
|
||||||
// in case there is no input and no group_bys we need to return true
|
if (aggregation_.empty()) {
|
||||||
// just this once
|
|
||||||
if (aggregation_.empty() && self_.group_by_.empty()) {
|
|
||||||
auto *pull_memory = context.evaluation_context.memory;
|
auto *pull_memory = context.evaluation_context.memory;
|
||||||
// place default aggregation values on the frame
|
// place default aggregation values on the frame
|
||||||
for (const auto &elem : self_.aggregations_)
|
for (const auto &elem : self_.aggregations_)
|
||||||
|
@ -153,10 +153,10 @@ std::vector<std::pair<Identifier, TriggerIdentifierTag>> GetPredefinedIdentifier
|
|||||||
Trigger::Trigger(std::string name, const std::string &query,
|
Trigger::Trigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||||
DbAccessor *db_accessor, utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
DbAccessor *db_accessor, const InterpreterConfig::Query &query_config,
|
||||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker)
|
std::optional<std::string> owner, const query::AuthChecker *auth_checker)
|
||||||
: name_{std::move(name)},
|
: name_{std::move(name)},
|
||||||
parsed_statements_{ParseQuery(query, user_parameters, query_cache, antlr_lock, query_config)},
|
parsed_statements_{ParseQuery(query, user_parameters, query_cache, query_config)},
|
||||||
event_type_{event_type},
|
event_type_{event_type},
|
||||||
owner_{std::move(owner)} {
|
owner_{std::move(owner)} {
|
||||||
// We check immediately if the query is valid by trying to create a plan.
|
// We check immediately if the query is valid by trying to create a plan.
|
||||||
@ -257,7 +257,7 @@ inline constexpr uint64_t kVersion{2};
|
|||||||
TriggerStore::TriggerStore(std::filesystem::path directory) : storage_{std::move(directory)} {}
|
TriggerStore::TriggerStore(std::filesystem::path directory) : storage_{std::move(directory)} {}
|
||||||
|
|
||||||
void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config,
|
||||||
const query::AuthChecker *auth_checker) {
|
const query::AuthChecker *auth_checker) {
|
||||||
MG_ASSERT(before_commit_triggers_.size() == 0 && after_commit_triggers_.size() == 0,
|
MG_ASSERT(before_commit_triggers_.size() == 0 && after_commit_triggers_.size() == 0,
|
||||||
"Cannot restore trigger when some triggers already exist!");
|
"Cannot restore trigger when some triggers already exist!");
|
||||||
@ -317,8 +317,8 @@ void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache
|
|||||||
|
|
||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, antlr_lock,
|
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
query_config, std::move(owner), auth_checker);
|
std::move(owner), auth_checker);
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
||||||
continue;
|
continue;
|
||||||
@ -336,8 +336,8 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
TriggerEventType event_type, TriggerPhase phase,
|
TriggerEventType event_type, TriggerPhase phase,
|
||||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker) {
|
const query::AuthChecker *auth_checker) {
|
||||||
std::unique_lock store_guard{store_lock_};
|
std::unique_lock store_guard{store_lock_};
|
||||||
if (storage_.Get(name)) {
|
if (storage_.Get(name)) {
|
||||||
throw utils::BasicException("Trigger with the same name already exists.");
|
throw utils::BasicException("Trigger with the same name already exists.");
|
||||||
@ -345,8 +345,8 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
|
|
||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, antlr_lock,
|
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
query_config, std::move(owner), auth_checker);
|
std::move(owner), auth_checker);
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
||||||
std::stringstream identifier_names_stream;
|
std::stringstream identifier_names_stream;
|
||||||
|
@ -34,7 +34,7 @@ namespace memgraph::query {
|
|||||||
struct Trigger {
|
struct Trigger {
|
||||||
explicit Trigger(std::string name, const std::string &query,
|
explicit Trigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor, utils::SpinLock *antlr_lock,
|
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
const query::AuthChecker *auth_checker);
|
const query::AuthChecker *auth_checker);
|
||||||
|
|
||||||
@ -81,14 +81,13 @@ struct TriggerStore {
|
|||||||
explicit TriggerStore(std::filesystem::path directory);
|
explicit TriggerStore(std::filesystem::path directory);
|
||||||
|
|
||||||
void RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
void RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, const query::AuthChecker *auth_checker);
|
||||||
const query::AuthChecker *auth_checker);
|
|
||||||
|
|
||||||
void AddTrigger(std::string name, const std::string &query,
|
void AddTrigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||||
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker);
|
const query::AuthChecker *auth_checker);
|
||||||
|
|
||||||
void DropTrigger(const std::string &name);
|
void DropTrigger(const std::string &name);
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ add_custom_command(
|
|||||||
OUTPUT ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include}
|
OUTPUT ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include}
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
||||||
COMMAND
|
COMMAND
|
||||||
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.9.2-complete.jar
|
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.10.1-complete.jar
|
||||||
-Dlanguage=Cpp -visitor -package antlropencypher
|
-Dlanguage=Cpp -visitor -package antlropencypher
|
||||||
-o ${opencypher_generated}
|
-o ${opencypher_generated}
|
||||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||||
|
@ -21,8 +21,7 @@ namespace memgraph::query::v2 {
|
|||||||
CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan) : plan_(std::move(plan)) {}
|
CachedPlan::CachedPlan(std::unique_ptr<LogicalPlan> plan) : plan_(std::move(plan)) {}
|
||||||
|
|
||||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::v3::PropertyValue> ¶ms,
|
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::v3::PropertyValue> ¶ms,
|
||||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
utils::SkipList<QueryCacheEntry> *cache, const InterpreterConfig::Query &query_config) {
|
||||||
const InterpreterConfig::Query &query_config) {
|
|
||||||
// Strip the query for caching purposes. The process of stripping a query
|
// Strip the query for caching purposes. The process of stripping a query
|
||||||
// "normalizes" it by replacing any literals with new parameters. This
|
// "normalizes" it by replacing any literals with new parameters. This
|
||||||
// results in just the *structure* of the query being taken into account for
|
// results in just the *structure* of the query being taken into account for
|
||||||
@ -63,9 +62,6 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (it == accessor.end()) {
|
if (it == accessor.end()) {
|
||||||
{
|
|
||||||
std::unique_lock<utils::SpinLock> guard(*antlr_lock);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parser = std::make_unique<frontend::opencypher::Parser>(stripped_query.query());
|
parser = std::make_unique<frontend::opencypher::Parser>(stripped_query.query());
|
||||||
} catch (const SyntaxException &e) {
|
} catch (const SyntaxException &e) {
|
||||||
@ -77,7 +73,6 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
|||||||
// up.
|
// up.
|
||||||
LOG_FATAL("The stripped query can't be parsed, but the original can.");
|
LOG_FATAL("The stripped query can't be parsed, but the original can.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the ANTLR4 parse tree into an AST.
|
// Convert the ANTLR4 parse tree into an AST.
|
||||||
AstStorage ast_storage;
|
AstStorage ast_storage;
|
||||||
|
@ -111,8 +111,7 @@ struct ParsedQuery {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::v3::PropertyValue> ¶ms,
|
ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::string, storage::v3::PropertyValue> ¶ms,
|
||||||
utils::SkipList<QueryCacheEntry> *cache, utils::SpinLock *antlr_lock,
|
utils::SkipList<QueryCacheEntry> *cache, const InterpreterConfig::Query &query_config);
|
||||||
const InterpreterConfig::Query &query_config);
|
|
||||||
|
|
||||||
class SingleNodeLogicalPlan final : public LogicalPlan {
|
class SingleNodeLogicalPlan final : public LogicalPlan {
|
||||||
public:
|
public:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -115,7 +115,7 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
auto operators = ExtractOperators(all_children, allowed_operators);
|
auto operators = ExtractOperators(all_children, allowed_operators);
|
||||||
|
|
||||||
for (auto *expression : _expressions) {
|
for (auto *expression : _expressions) {
|
||||||
expressions.push_back(expression->accept(this));
|
expressions.push_back(std::any_cast<Expression *>(expression->accept(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression *first_operand = expressions[0];
|
Expression *first_operand = expressions[0];
|
||||||
@ -131,7 +131,7 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
DMG_ASSERT(_expression, "can't happen");
|
DMG_ASSERT(_expression, "can't happen");
|
||||||
auto operators = ExtractOperators(all_children, allowed_operators);
|
auto operators = ExtractOperators(all_children, allowed_operators);
|
||||||
|
|
||||||
Expression *expression = _expression->accept(this);
|
Expression *expression = std::any_cast<Expression *>(_expression->accept(this));
|
||||||
for (int i = (int)operators.size() - 1; i >= 0; --i) {
|
for (int i = (int)operators.size() - 1; i >= 0; --i) {
|
||||||
expression = CreateUnaryOperatorByToken(operators[i], expression);
|
expression = CreateUnaryOperatorByToken(operators[i], expression);
|
||||||
}
|
}
|
||||||
|
@ -1186,7 +1186,7 @@ PreparedQuery PrepareExplainQuery(ParsedQuery parsed_query, std::map<std::string
|
|||||||
// full query string) when given just the inner query to execute.
|
// full query string) when given just the inner query to execute.
|
||||||
ParsedQuery parsed_inner_query =
|
ParsedQuery parsed_inner_query =
|
||||||
ParseQuery(parsed_query.query_string.substr(kExplainQueryStart.size()), parsed_query.user_parameters,
|
ParseQuery(parsed_query.query_string.substr(kExplainQueryStart.size()), parsed_query.user_parameters,
|
||||||
&interpreter_context->ast_cache, &interpreter_context->antlr_lock, interpreter_context->config.query);
|
&interpreter_context->ast_cache, interpreter_context->config.query);
|
||||||
|
|
||||||
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
||||||
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in EXPLAIN");
|
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in EXPLAIN");
|
||||||
@ -1253,7 +1253,7 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
// full query string) when given just the inner query to execute.
|
// full query string) when given just the inner query to execute.
|
||||||
ParsedQuery parsed_inner_query =
|
ParsedQuery parsed_inner_query =
|
||||||
ParseQuery(parsed_query.query_string.substr(kProfileQueryStart.size()), parsed_query.user_parameters,
|
ParseQuery(parsed_query.query_string.substr(kProfileQueryStart.size()), parsed_query.user_parameters,
|
||||||
&interpreter_context->ast_cache, &interpreter_context->antlr_lock, interpreter_context->config.query);
|
&interpreter_context->ast_cache, interpreter_context->config.query);
|
||||||
|
|
||||||
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_inner_query.query);
|
||||||
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in PROFILE");
|
MG_ASSERT(cypher_query, "Cypher grammar should not allow other queries in PROFILE");
|
||||||
@ -1571,8 +1571,7 @@ Callback CreateTrigger(TriggerQuery *trigger_query,
|
|||||||
interpreter_context->trigger_store.AddTrigger(
|
interpreter_context->trigger_store.AddTrigger(
|
||||||
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
|
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
|
||||||
dba, &interpreter_context->antlr_lock, interpreter_context->config.query, std::move(owner),
|
dba, interpreter_context->config.query, std::move(owner), interpreter_context->auth_checker);
|
||||||
interpreter_context->auth_checker);
|
|
||||||
return {};
|
return {};
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -2129,8 +2128,8 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
query_execution->summary["cost_estimate"] = 0.0;
|
query_execution->summary["cost_estimate"] = 0.0;
|
||||||
|
|
||||||
utils::Timer parsing_timer;
|
utils::Timer parsing_timer;
|
||||||
ParsedQuery parsed_query = ParseQuery(query_string, params, &interpreter_context_->ast_cache,
|
ParsedQuery parsed_query =
|
||||||
&interpreter_context_->antlr_lock, interpreter_context_->config.query);
|
ParseQuery(query_string, params, &interpreter_context_->ast_cache, interpreter_context_->config.query);
|
||||||
query_execution->summary["parsing_time"] = parsing_timer.Elapsed().count();
|
query_execution->summary["parsing_time"] = parsing_timer.Elapsed().count();
|
||||||
|
|
||||||
// Some queries require an active transaction in order to be prepared.
|
// Some queries require an active transaction in order to be prepared.
|
||||||
|
@ -171,13 +171,6 @@ struct InterpreterContext {
|
|||||||
|
|
||||||
storage::v3::Storage *db;
|
storage::v3::Storage *db;
|
||||||
|
|
||||||
// ANTLR has singleton instance that is shared between threads. It is
|
|
||||||
// protected by locks inside of ANTLR. Unfortunately, they are not protected
|
|
||||||
// in a very good way. Once we have ANTLR version without race conditions we
|
|
||||||
// can remove this lock. This will probably never happen since ANTLR
|
|
||||||
// developers introduce more bugs in each version. Fortunately, we have
|
|
||||||
// cache so this lock probably won't impact performance much...
|
|
||||||
utils::SpinLock antlr_lock;
|
|
||||||
std::optional<double> tsc_frequency{utils::GetTSCFrequency()};
|
std::optional<double> tsc_frequency{utils::GetTSCFrequency()};
|
||||||
std::atomic<bool> is_shutting_down{false};
|
std::atomic<bool> is_shutting_down{false};
|
||||||
|
|
||||||
|
@ -153,10 +153,10 @@ std::vector<std::pair<Identifier, TriggerIdentifierTag>> GetPredefinedIdentifier
|
|||||||
Trigger::Trigger(std::string name, const std::string &query,
|
Trigger::Trigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
||||||
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||||
DbAccessor *db_accessor, utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
DbAccessor *db_accessor, const InterpreterConfig::Query &query_config,
|
||||||
std::optional<std::string> owner, const query::v2::AuthChecker *auth_checker)
|
std::optional<std::string> owner, const query::v2::AuthChecker *auth_checker)
|
||||||
: name_{std::move(name)},
|
: name_{std::move(name)},
|
||||||
parsed_statements_{ParseQuery(query, user_parameters, query_cache, antlr_lock, query_config)},
|
parsed_statements_{ParseQuery(query, user_parameters, query_cache, query_config)},
|
||||||
event_type_{event_type},
|
event_type_{event_type},
|
||||||
owner_{std::move(owner)} {
|
owner_{std::move(owner)} {
|
||||||
// We check immediately if the query is valid by trying to create a plan.
|
// We check immediately if the query is valid by trying to create a plan.
|
||||||
@ -257,7 +257,7 @@ inline constexpr uint64_t kVersion{2};
|
|||||||
TriggerStore::TriggerStore(std::filesystem::path directory) : storage_{std::move(directory)} {}
|
TriggerStore::TriggerStore(std::filesystem::path directory) : storage_{std::move(directory)} {}
|
||||||
|
|
||||||
void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config,
|
||||||
const query::v2::AuthChecker *auth_checker) {
|
const query::v2::AuthChecker *auth_checker) {
|
||||||
MG_ASSERT(before_commit_triggers_.size() == 0 && after_commit_triggers_.size() == 0,
|
MG_ASSERT(before_commit_triggers_.size() == 0 && after_commit_triggers_.size() == 0,
|
||||||
"Cannot restore trigger when some triggers already exist!");
|
"Cannot restore trigger when some triggers already exist!");
|
||||||
@ -317,8 +317,8 @@ void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache
|
|||||||
|
|
||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, antlr_lock,
|
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
query_config, std::move(owner), auth_checker);
|
std::move(owner), auth_checker);
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
||||||
continue;
|
continue;
|
||||||
@ -336,8 +336,8 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
||||||
TriggerEventType event_type, TriggerPhase phase,
|
TriggerEventType event_type, TriggerPhase phase,
|
||||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
std::optional<std::string> owner, const query::v2::AuthChecker *auth_checker) {
|
const query::v2::AuthChecker *auth_checker) {
|
||||||
std::unique_lock store_guard{store_lock_};
|
std::unique_lock store_guard{store_lock_};
|
||||||
if (storage_.Get(name)) {
|
if (storage_.Get(name)) {
|
||||||
throw utils::BasicException("Trigger with the same name already exists.");
|
throw utils::BasicException("Trigger with the same name already exists.");
|
||||||
@ -345,8 +345,8 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
|
|
||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, antlr_lock,
|
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
query_config, std::move(owner), auth_checker);
|
std::move(owner), auth_checker);
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
||||||
std::stringstream identifier_names_stream;
|
std::stringstream identifier_names_stream;
|
||||||
|
@ -35,8 +35,8 @@ struct Trigger {
|
|||||||
explicit Trigger(std::string name, const std::string &query,
|
explicit Trigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::v3::PropertyValue> &user_parameters,
|
||||||
TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
std::optional<std::string> owner, const AuthChecker *auth_checker);
|
const query::v2::AuthChecker *auth_checker);
|
||||||
|
|
||||||
void Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory, double max_execution_time_sec,
|
void Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory, double max_execution_time_sec,
|
||||||
std::atomic<bool> *is_shutting_down, const TriggerContext &context,
|
std::atomic<bool> *is_shutting_down, const TriggerContext &context,
|
||||||
@ -81,14 +81,13 @@ struct TriggerStore {
|
|||||||
explicit TriggerStore(std::filesystem::path directory);
|
explicit TriggerStore(std::filesystem::path directory);
|
||||||
|
|
||||||
void RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
void RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, const query::v2::AuthChecker *auth_checker);
|
||||||
const query::v2::AuthChecker *auth_checker);
|
|
||||||
|
|
||||||
void AddTrigger(std::string name, const std::string &query,
|
void AddTrigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::v3::PropertyValue> &user_parameters, TriggerEventType event_type,
|
const std::map<std::string, storage::v3::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||||
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
utils::SpinLock *antlr_lock, const InterpreterConfig::Query &query_config,
|
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
||||||
std::optional<std::string> owner, const query::v2::AuthChecker *auth_checker);
|
const query::v2::AuthChecker *auth_checker);
|
||||||
|
|
||||||
void DropTrigger(const std::string &name);
|
void DropTrigger(const std::string &name);
|
||||||
|
|
||||||
|
@ -73,6 +73,16 @@ Feature: Aggregations
|
|||||||
| n |
|
| n |
|
||||||
| 5 |
|
| 5 |
|
||||||
|
|
||||||
|
Scenario: Count test 07:
|
||||||
|
Given an empty graph
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
RETURN count(null)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| count(null) |
|
||||||
|
| 0 |
|
||||||
|
|
||||||
Scenario: Sum test 01:
|
Scenario: Sum test 01:
|
||||||
Given an empty graph
|
Given an empty graph
|
||||||
And having executed
|
And having executed
|
||||||
@ -114,6 +124,16 @@ Feature: Aggregations
|
|||||||
| 4 | 0 |
|
| 4 | 0 |
|
||||||
| 4 | 1 |
|
| 4 | 1 |
|
||||||
|
|
||||||
|
Scenario: Sum test 04:
|
||||||
|
Given an empty graph
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
RETURN sum(null)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| sum(null) |
|
||||||
|
| 0 |
|
||||||
|
|
||||||
Scenario: Avg test 01:
|
Scenario: Avg test 01:
|
||||||
Given an empty graph
|
Given an empty graph
|
||||||
And having executed
|
And having executed
|
||||||
@ -155,6 +175,16 @@ Feature: Aggregations
|
|||||||
| 2.0 | 0 |
|
| 2.0 | 0 |
|
||||||
| 4.0 | 1 |
|
| 4.0 | 1 |
|
||||||
|
|
||||||
|
Scenario: Avg test 04:
|
||||||
|
Given an empty graph
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
RETURN avg(null)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| avg(null) |
|
||||||
|
| null |
|
||||||
|
|
||||||
Scenario: Min test 01:
|
Scenario: Min test 01:
|
||||||
Given an empty graph
|
Given an empty graph
|
||||||
And having executed
|
And having executed
|
||||||
@ -196,6 +226,16 @@ Feature: Aggregations
|
|||||||
| 1 | 0 |
|
| 1 | 0 |
|
||||||
| 4 | 1 |
|
| 4 | 1 |
|
||||||
|
|
||||||
|
Scenario: Min test 04:
|
||||||
|
Given an empty graph
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
RETURN min(null)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| min(null) |
|
||||||
|
| null |
|
||||||
|
|
||||||
Scenario: Max test 01:
|
Scenario: Max test 01:
|
||||||
Given an empty graph
|
Given an empty graph
|
||||||
And having executed
|
And having executed
|
||||||
@ -237,6 +277,16 @@ Feature: Aggregations
|
|||||||
| 3 | 0 |
|
| 3 | 0 |
|
||||||
| 4 | 1 |
|
| 4 | 1 |
|
||||||
|
|
||||||
|
Scenario: Max test 04:
|
||||||
|
Given an empty graph
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
RETURN max(null)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| max(null) |
|
||||||
|
| null |
|
||||||
|
|
||||||
Scenario: Collect test 01:
|
Scenario: Collect test 01:
|
||||||
Given an empty graph
|
Given an empty graph
|
||||||
And having executed
|
And having executed
|
||||||
@ -279,3 +329,18 @@ Feature: Aggregations
|
|||||||
| n |
|
| n |
|
||||||
| {a_key: 13, b_key: 11, c_key: 12} |
|
| {a_key: 13, b_key: 11, c_key: 12} |
|
||||||
|
|
||||||
|
Scenario: Combined aggregations - some evauluates to null:
|
||||||
|
Given an empty graph
|
||||||
|
And having executed
|
||||||
|
"""
|
||||||
|
CREATE (f)
|
||||||
|
CREATE (n {property: 1})
|
||||||
|
"""
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
MATCH (n) RETURN count(n) < n.property, count(n.property), count(n), avg(n.property), min(n.property), max(n.property), sum(n.property)
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| count(n) < n.property | count(n.property) | count(n) | avg(n.property) | min(n.property) | max(n.property) | sum(n.property) |
|
||||||
|
| false | 1 | 1 | 1.0 | 1 | 1 | 1 |
|
||||||
|
| null | 0 | 1 | null | null | null | 0 |
|
||||||
|
@ -20,6 +20,15 @@
|
|||||||
#include "utils/signals.hpp"
|
#include "utils/signals.hpp"
|
||||||
#include "utils/stacktrace.hpp"
|
#include "utils/stacktrace.hpp"
|
||||||
|
|
||||||
|
// This test was introduced because Antlr Cpp runtime doesn't work well in a
|
||||||
|
// highly concurrent environment. Interpreter `interpret.hpp` contains
|
||||||
|
// `antlr_lock` used to avoid crashes.
|
||||||
|
// v4.6 and before -> Crashes.
|
||||||
|
// v4.8 -> Does NOT crash but sometimes this tests does NOT finish.
|
||||||
|
// Looks like a deadlock. -> The lock is still REQUIRED.
|
||||||
|
// v4.9 -> Seems to be working.
|
||||||
|
// v4.10 -> Seems to be working as well. -> antlr_lock removed
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
TEST(Antlr, Sigsegv) {
|
TEST(Antlr, Sigsegv) {
|
||||||
|
@ -17,3 +17,6 @@ endfunction(add_stress_test)
|
|||||||
|
|
||||||
add_stress_test(long_running.cpp)
|
add_stress_test(long_running.cpp)
|
||||||
target_link_libraries(${test_prefix}long_running mg-communication mg-io mg-utils)
|
target_link_libraries(${test_prefix}long_running mg-communication mg-io mg-utils)
|
||||||
|
|
||||||
|
add_stress_test(parser.cpp)
|
||||||
|
target_link_libraries(${test_prefix}parser mg-communication mg-io mg-utils mgclient)
|
||||||
|
@ -26,6 +26,11 @@ SMALL_DATASET = [
|
|||||||
"options": ["--vertex-count", "40000", "--create-pack-size", "100"],
|
"options": ["--vertex-count", "40000", "--create-pack-size", "100"],
|
||||||
"timeout": 5,
|
"timeout": 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"test": "parser.cpp",
|
||||||
|
"options": ["--per-worker-query-count", "1000"],
|
||||||
|
"timeout": 5,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"test": "long_running.cpp",
|
"test": "long_running.cpp",
|
||||||
"options": ["--vertex-count", "1000", "--edge-count", "5000", "--max-time", "1", "--verify", "20"],
|
"options": ["--vertex-count", "1000", "--edge-count", "5000", "--max-time", "1", "--verify", "20"],
|
||||||
@ -42,7 +47,8 @@ SMALL_DATASET = [
|
|||||||
# bipartite.py and create_match.py run for approx. 15min
|
# bipartite.py and create_match.py run for approx. 15min
|
||||||
# long_running runs for 5min x 6 times = 30min
|
# long_running runs for 5min x 6 times = 30min
|
||||||
# long_running runs for 8h
|
# long_running runs for 8h
|
||||||
LARGE_DATASET = [
|
LARGE_DATASET = (
|
||||||
|
[
|
||||||
{
|
{
|
||||||
"test": "bipartite.py",
|
"test": "bipartite.py",
|
||||||
"options": ["--u-count", "300", "--v-count", "300"],
|
"options": ["--u-count", "300", "--v-count", "300"],
|
||||||
@ -53,19 +59,23 @@ LARGE_DATASET = [
|
|||||||
"options": ["--vertex-count", "500000", "--create-pack-size", "500"],
|
"options": ["--vertex-count", "500000", "--create-pack-size", "500"],
|
||||||
"timeout": 30,
|
"timeout": 30,
|
||||||
},
|
},
|
||||||
] + [
|
]
|
||||||
|
+ [
|
||||||
{
|
{
|
||||||
"test": "long_running.cpp",
|
"test": "long_running.cpp",
|
||||||
"options": ["--vertex-count", "10000", "--edge-count", "40000", "--max-time", "5", "--verify", "60"],
|
"options": ["--vertex-count", "10000", "--edge-count", "40000", "--max-time", "5", "--verify", "60"],
|
||||||
"timeout": 16,
|
"timeout": 16,
|
||||||
},
|
},
|
||||||
] * 6 + [
|
]
|
||||||
|
* 6
|
||||||
|
+ [
|
||||||
{
|
{
|
||||||
"test": "long_running.cpp",
|
"test": "long_running.cpp",
|
||||||
"options": ["--vertex-count", "200000", "--edge-count", "1000000", "--max-time", "480", "--verify", "300"],
|
"options": ["--vertex-count", "200000", "--edge-count", "1000000", "--max-time", "480", "--verify", "300"],
|
||||||
"timeout": 500,
|
"timeout": 500,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# paths
|
# paths
|
||||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
@ -101,8 +111,7 @@ def run_test(args, test, options, timeout):
|
|||||||
# find binary
|
# find binary
|
||||||
if test.endswith(".py"):
|
if test.endswith(".py"):
|
||||||
logging = "DEBUG" if args.verbose else "WARNING"
|
logging = "DEBUG" if args.verbose else "WARNING"
|
||||||
binary = [args.python, "-u", os.path.join(SCRIPT_DIR, test),
|
binary = [args.python, "-u", os.path.join(SCRIPT_DIR, test), "--logging", logging]
|
||||||
"--logging", logging]
|
|
||||||
elif test.endswith(".cpp"):
|
elif test.endswith(".cpp"):
|
||||||
exe = os.path.join(BUILD_DIR, "tests", "stress", test[:-4])
|
exe = os.path.join(BUILD_DIR, "tests", "stress", test[:-4])
|
||||||
binary = [exe]
|
binary = [exe]
|
||||||
@ -115,8 +124,7 @@ def run_test(args, test, options, timeout):
|
|||||||
ret_test = subprocess.run(cmd, cwd=SCRIPT_DIR, timeout=timeout * 60)
|
ret_test = subprocess.run(cmd, cwd=SCRIPT_DIR, timeout=timeout * 60)
|
||||||
|
|
||||||
if ret_test.returncode != 0:
|
if ret_test.returncode != 0:
|
||||||
raise Exception("Test '{}' binary returned non-zero ({})!".format(
|
raise Exception("Test '{}' binary returned non-zero ({})!".format(test, ret_test.returncode))
|
||||||
test, ret_test.returncode))
|
|
||||||
|
|
||||||
runtime = time.time() - start
|
runtime = time.time() - start
|
||||||
print(" Done after {:.3f} seconds".format(runtime))
|
print(" Done after {:.3f} seconds".format(runtime))
|
||||||
@ -126,38 +134,53 @@ def run_test(args, test, options, timeout):
|
|||||||
|
|
||||||
# parse arguments
|
# parse arguments
|
||||||
parser = argparse.ArgumentParser(description="Run stress tests on Memgraph.")
|
parser = argparse.ArgumentParser(description="Run stress tests on Memgraph.")
|
||||||
parser.add_argument("--memgraph", default = os.path.join(BUILD_DIR,
|
parser.add_argument("--memgraph", default=os.path.join(BUILD_DIR, "memgraph"))
|
||||||
"memgraph"))
|
|
||||||
parser.add_argument("--log-file", default="")
|
parser.add_argument("--log-file", default="")
|
||||||
parser.add_argument("--data-directory", default="")
|
parser.add_argument("--data-directory", default="")
|
||||||
parser.add_argument("--python", default = os.path.join(SCRIPT_DIR,
|
parser.add_argument("--python", default=os.path.join(SCRIPT_DIR, "ve3", "bin", "python3"), type=str)
|
||||||
"ve3", "bin", "python3"), type = str)
|
parser.add_argument("--large-dataset", action="store_const", const=True, default=False)
|
||||||
parser.add_argument("--large-dataset", action = "store_const",
|
parser.add_argument("--use-ssl", action="store_const", const=True, default=False)
|
||||||
const = True, default = False)
|
parser.add_argument("--verbose", action="store_const", const=True, default=False)
|
||||||
parser.add_argument("--use-ssl", action = "store_const",
|
|
||||||
const = True, default = False)
|
|
||||||
parser.add_argument("--verbose", action = "store_const",
|
|
||||||
const = True, default = False)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# generate temporary SSL certs
|
# generate temporary SSL certs
|
||||||
if args.use_ssl:
|
if args.use_ssl:
|
||||||
# https://unix.stackexchange.com/questions/104171/create-ssl-certificate-non-interactively
|
# https://unix.stackexchange.com/questions/104171/create-ssl-certificate-non-interactively
|
||||||
subj = "/C=HR/ST=Zagreb/L=Zagreb/O=Memgraph/CN=db.memgraph.com"
|
subj = "/C=HR/ST=Zagreb/L=Zagreb/O=Memgraph/CN=db.memgraph.com"
|
||||||
subprocess.run(["openssl", "req", "-new", "-newkey", "rsa:4096",
|
subprocess.run(
|
||||||
"-days", "365", "-nodes", "-x509", "-subj", subj,
|
[
|
||||||
"-keyout", KEY_FILE, "-out", CERT_FILE], check=True)
|
"openssl",
|
||||||
|
"req",
|
||||||
|
"-new",
|
||||||
|
"-newkey",
|
||||||
|
"rsa:4096",
|
||||||
|
"-days",
|
||||||
|
"365",
|
||||||
|
"-nodes",
|
||||||
|
"-x509",
|
||||||
|
"-subj",
|
||||||
|
subj,
|
||||||
|
"-keyout",
|
||||||
|
KEY_FILE,
|
||||||
|
"-out",
|
||||||
|
CERT_FILE,
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
# start memgraph
|
# start memgraph
|
||||||
cwd = os.path.dirname(args.memgraph)
|
cwd = os.path.dirname(args.memgraph)
|
||||||
cmd = [args.memgraph, "--bolt-num-workers=" + str(THREADS),
|
cmd = [
|
||||||
|
args.memgraph,
|
||||||
|
"--bolt-num-workers=" + str(THREADS),
|
||||||
"--storage-properties-on-edges=true",
|
"--storage-properties-on-edges=true",
|
||||||
"--storage-snapshot-on-exit=true",
|
"--storage-snapshot-on-exit=true",
|
||||||
"--storage-snapshot-interval-sec=600",
|
"--storage-snapshot-interval-sec=600",
|
||||||
"--storage-snapshot-retention-count=1",
|
"--storage-snapshot-retention-count=1",
|
||||||
"--storage-wal-enabled=true",
|
"--storage-wal-enabled=true",
|
||||||
"--storage-recover-on-startup=false",
|
"--storage-recover-on-startup=false",
|
||||||
"--query-execution-timeout-sec=1200"]
|
"--query-execution-timeout-sec=1200",
|
||||||
|
]
|
||||||
if not args.verbose:
|
if not args.verbose:
|
||||||
cmd += ["--log-level", "WARNING"]
|
cmd += ["--log-level", "WARNING"]
|
||||||
if args.log_file:
|
if args.log_file:
|
||||||
@ -174,10 +197,12 @@ assert proc_mg.poll() is None, "The database binary died prematurely!"
|
|||||||
@atexit.register
|
@atexit.register
|
||||||
def cleanup():
|
def cleanup():
|
||||||
global proc_mg
|
global proc_mg
|
||||||
if proc_mg.poll() != None: return
|
if proc_mg.poll() != None:
|
||||||
|
return
|
||||||
proc_mg.kill()
|
proc_mg.kill()
|
||||||
proc_mg.wait()
|
proc_mg.wait()
|
||||||
|
|
||||||
|
|
||||||
# run tests
|
# run tests
|
||||||
runtimes = {}
|
runtimes = {}
|
||||||
dataset = LARGE_DATASET if args.large_dataset else SMALL_DATASET
|
dataset = LARGE_DATASET if args.large_dataset else SMALL_DATASET
|
||||||
|
79
tests/stress/parser.cpp
Normal file
79
tests/stress/parser.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2022 Memgraph Ltd.
|
||||||
|
//
|
||||||
|
// Use of this software is governed by the Business Source License
|
||||||
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
|
// License, and you may not use this file except in compliance with the Business Source License.
|
||||||
|
//
|
||||||
|
// As of the Change Date specified in that file, in accordance with
|
||||||
|
// the Business Source License, use of this software will be governed
|
||||||
|
// by the Apache License, Version 2.0, included in the file
|
||||||
|
// licenses/APL.txt.
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <random>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
#include "communication/bolt/client.hpp"
|
||||||
|
#include "io/network/endpoint.hpp"
|
||||||
|
#include "mgclient.hpp"
|
||||||
|
#include "utils/timer.hpp"
|
||||||
|
|
||||||
|
DEFINE_string(address, "127.0.0.1", "Server address");
|
||||||
|
DEFINE_int32(port, 7687, "Server port");
|
||||||
|
DEFINE_string(username, "", "Username for the database");
|
||||||
|
DEFINE_string(password, "", "Password for the database");
|
||||||
|
DEFINE_bool(use_ssl, false, "Set to true to connect with SSL to the server.");
|
||||||
|
DEFINE_int32(worker_count, 1, "The number of concurrent workers executing queries against the server.");
|
||||||
|
DEFINE_int32(per_worker_query_count, 100, "The number of queries each worker will try to execute.");
|
||||||
|
|
||||||
|
auto make_client() {
|
||||||
|
mg::Client::Params params;
|
||||||
|
params.host = FLAGS_address;
|
||||||
|
params.port = static_cast<uint16_t>(FLAGS_port);
|
||||||
|
params.use_ssl = FLAGS_use_ssl;
|
||||||
|
return mg::Client::Connect(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
mg::Client::Init();
|
||||||
|
|
||||||
|
spdlog::info("Cleaning the database instance...");
|
||||||
|
auto client = make_client();
|
||||||
|
client->Execute("MATCH (n) DETACH DELETE n");
|
||||||
|
client->DiscardAll();
|
||||||
|
|
||||||
|
spdlog::info(fmt::format("Starting parser stress test with {} workers and {} queries per worker...",
|
||||||
|
FLAGS_worker_count, FLAGS_per_worker_query_count));
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
memgraph::utils::Timer timer;
|
||||||
|
for (int i = 0; i < FLAGS_worker_count; ++i) {
|
||||||
|
threads.push_back(std::thread([]() {
|
||||||
|
auto client = make_client();
|
||||||
|
std::mt19937 generator{std::random_device{}()};
|
||||||
|
std::uniform_int_distribution<uint64_t> distribution{std::numeric_limits<uint64_t>::min(),
|
||||||
|
std::numeric_limits<uint64_t>::max()};
|
||||||
|
for (int i = 0; i < FLAGS_per_worker_query_count; ++i) {
|
||||||
|
try {
|
||||||
|
auto is_executed = client->Execute(fmt::format("MATCH (n:Label{}) RETURN n;", distribution(generator)));
|
||||||
|
if (!is_executed) {
|
||||||
|
LOG_FATAL("One of the parser stress test queries failed.");
|
||||||
|
}
|
||||||
|
client->FetchAll();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LOG_FATAL("One of the parser stress test queries failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ranges::for_each(threads, [](auto &t) { t.join(); });
|
||||||
|
spdlog::info(
|
||||||
|
fmt::format("All queries executed in {:.4f}s. The parser managed to handle the load.", timer.Elapsed().count()));
|
||||||
|
mg::Client::Finalize();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -217,31 +217,40 @@ TEST_F(QueryPlanAggregateOps, WithData) {
|
|||||||
TEST_F(QueryPlanAggregateOps, WithoutDataWithGroupBy) {
|
TEST_F(QueryPlanAggregateOps, WithoutDataWithGroupBy) {
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::COUNT});
|
auto results = AggregationResults(true, {Aggregation::Op::COUNT});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
|
||||||
|
EXPECT_EQ(results[0][0].ValueInt(), 0);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::SUM});
|
auto results = AggregationResults(true, {Aggregation::Op::SUM});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
|
||||||
|
EXPECT_EQ(results[0][0].ValueInt(), 0);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::AVG});
|
auto results = AggregationResults(true, {Aggregation::Op::AVG});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::MIN});
|
auto results = AggregationResults(true, {Aggregation::Op::MIN});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::MAX});
|
auto results = AggregationResults(true, {Aggregation::Op::MAX});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_LIST});
|
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_LIST});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::List);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_MAP});
|
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_MAP});
|
||||||
EXPECT_EQ(results.size(), 0);
|
EXPECT_EQ(results.size(), 1);
|
||||||
|
EXPECT_EQ(results[0][0].type(), TypedValue::Type::Map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +269,7 @@ TEST_F(QueryPlanAggregateOps, WithoutDataWithoutGroupBy) {
|
|||||||
// max
|
// max
|
||||||
EXPECT_TRUE(results[0][3].IsNull());
|
EXPECT_TRUE(results[0][3].IsNull());
|
||||||
// sum
|
// sum
|
||||||
EXPECT_TRUE(results[0][4].IsNull());
|
EXPECT_EQ(results[0][4].ValueInt(), 0);
|
||||||
// avg
|
// avg
|
||||||
EXPECT_TRUE(results[0][5].IsNull());
|
EXPECT_TRUE(results[0][5].IsNull());
|
||||||
// collect list
|
// collect list
|
||||||
|
@ -891,7 +891,6 @@ class TriggerStoreTest : public ::testing::Test {
|
|||||||
std::optional<memgraph::query::DbAccessor> dba;
|
std::optional<memgraph::query::DbAccessor> dba;
|
||||||
|
|
||||||
memgraph::utils::SkipList<memgraph::query::QueryCacheEntry> ast_cache;
|
memgraph::utils::SkipList<memgraph::query::QueryCacheEntry> ast_cache;
|
||||||
memgraph::utils::SpinLock antlr_lock;
|
|
||||||
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -909,7 +908,7 @@ TEST_F(TriggerStoreTest, Restore) {
|
|||||||
|
|
||||||
const auto reset_store = [&] {
|
const auto reset_store = [&] {
|
||||||
store.emplace(testing_directory);
|
store.emplace(testing_directory);
|
||||||
store->RestoreTriggers(&ast_cache, &*dba, &antlr_lock, memgraph::query::InterpreterConfig::Query{}, &auth_checker);
|
store->RestoreTriggers(&ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, &auth_checker);
|
||||||
};
|
};
|
||||||
|
|
||||||
reset_store();
|
reset_store();
|
||||||
@ -930,12 +929,12 @@ TEST_F(TriggerStoreTest, Restore) {
|
|||||||
store->AddTrigger(
|
store->AddTrigger(
|
||||||
trigger_name_before, trigger_statement,
|
trigger_name_before, trigger_statement,
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
||||||
event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||||
store->AddTrigger(
|
store->AddTrigger(
|
||||||
trigger_name_after, trigger_statement,
|
trigger_name_after, trigger_statement,
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{"value"}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{"value"}}},
|
||||||
event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, {owner}, &auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, {owner}, &auth_checker);
|
||||||
|
|
||||||
const auto check_triggers = [&] {
|
const auto check_triggers = [&] {
|
||||||
@ -986,16 +985,16 @@ TEST_F(TriggerStoreTest, AddTrigger) {
|
|||||||
|
|
||||||
// Invalid query in statements
|
// Invalid query in statements
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
@ -1003,15 +1002,15 @@ TEST_F(TriggerStoreTest, AddTrigger) {
|
|||||||
"trigger", "RETURN $parameter",
|
"trigger", "RETURN $parameter",
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
||||||
memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
&antlr_lock, memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
||||||
|
|
||||||
// Inserting with the same name
|
// Inserting with the same name
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
@ -1027,7 +1026,7 @@ TEST_F(TriggerStoreTest, DropTrigger) {
|
|||||||
|
|
||||||
const auto *trigger_name = "trigger";
|
const auto *trigger_name = "trigger";
|
||||||
store.AddTrigger(trigger_name, "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
store.AddTrigger(trigger_name, "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||||
|
|
||||||
ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
|
ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
|
||||||
@ -1040,7 +1039,7 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
|
|||||||
|
|
||||||
std::vector<memgraph::query::TriggerStore::TriggerInfo> expected_info;
|
std::vector<memgraph::query::TriggerStore::TriggerInfo> expected_info;
|
||||||
store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||||
expected_info.push_back({"trigger", "RETURN 1", memgraph::query::TriggerEventType::VERTEX_CREATE,
|
expected_info.push_back({"trigger", "RETURN 1", memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT});
|
memgraph::query::TriggerPhase::BEFORE_COMMIT});
|
||||||
@ -1060,7 +1059,7 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
|
|||||||
check_trigger_info();
|
check_trigger_info();
|
||||||
|
|
||||||
store.AddTrigger("edge_update_trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::EDGE_UPDATE,
|
store.AddTrigger("edge_update_trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
|
||||||
expected_info.push_back({"edge_update_trigger", "RETURN 1", memgraph::query::TriggerEventType::EDGE_UPDATE,
|
expected_info.push_back({"edge_update_trigger", "RETURN 1", memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT});
|
memgraph::query::TriggerPhase::AFTER_COMMIT});
|
||||||
@ -1174,7 +1173,7 @@ TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) {
|
|||||||
for (const auto keyword : keywords) {
|
for (const auto keyword : keywords) {
|
||||||
SCOPED_TRACE(keyword);
|
SCOPED_TRACE(keyword);
|
||||||
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
|
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
|
||||||
store.DropTrigger(trigger_name);
|
store.DropTrigger(trigger_name);
|
||||||
}
|
}
|
||||||
@ -1199,22 +1198,22 @@ TEST_F(TriggerStoreTest, AuthCheckerUsage) {
|
|||||||
|
|
||||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
|
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker));
|
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker));
|
||||||
|
|
||||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
|
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, owner, &mock_checker));
|
memgraph::query::InterpreterConfig::Query{}, owner, &mock_checker));
|
||||||
|
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::MATCH)))
|
EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::MATCH)))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(Return(false));
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
ASSERT_THROW(store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
|
ASSERT_THROW(
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba, &antlr_lock,
|
memgraph::query::TriggerEventType::EDGE_UPDATE, memgraph::query::TriggerPhase::AFTER_COMMIT,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
|
&ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
|
||||||
, memgraph::utils::BasicException);
|
, memgraph::utils::BasicException);
|
||||||
|
|
||||||
store.emplace(testing_directory);
|
store.emplace(testing_directory);
|
||||||
@ -1223,8 +1222,8 @@ TEST_F(TriggerStoreTest, AuthCheckerUsage) {
|
|||||||
.WillOnce(Return(false));
|
.WillOnce(Return(false));
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE))).Times(1).WillOnce(Return(true));
|
EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE))).Times(1).WillOnce(Return(true));
|
||||||
|
|
||||||
ASSERT_NO_THROW(store->RestoreTriggers(&ast_cache, &*dba, &antlr_lock, memgraph::query::InterpreterConfig::Query{},
|
ASSERT_NO_THROW(
|
||||||
&mock_checker));
|
store->RestoreTriggers(&ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, &mock_checker));
|
||||||
|
|
||||||
const auto triggers = store->GetTriggerInfo();
|
const auto triggers = store->GetTriggerInfo();
|
||||||
ASSERT_EQ(triggers.size(), 1);
|
ASSERT_EQ(triggers.size(), 1);
|
||||||
|
Loading…
Reference in New Issue
Block a user