Merge branch 'master' into Implement-constant-time-label-and-edge-type-retrieval
This commit is contained in:
@@ -73,6 +73,7 @@ add_subdirectory(constraints)
|
||||
add_subdirectory(inspect_query)
|
||||
add_subdirectory(filter_info)
|
||||
add_subdirectory(queries)
|
||||
add_subdirectory(query_modules_storage_modes)
|
||||
add_subdirectory(garbage_collection)
|
||||
add_subdirectory(query_planning)
|
||||
|
||||
|
||||
8
tests/e2e/query_modules_storage_modes/CMakeLists.txt
Normal file
8
tests/e2e/query_modules_storage_modes/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
function(copy_qm_storage_modes_e2e_python_files FILE_NAME)
|
||||
copy_e2e_python_files(query_modules_storage_modes ${FILE_NAME})
|
||||
endfunction()
|
||||
|
||||
copy_qm_storage_modes_e2e_python_files(common.py)
|
||||
copy_qm_storage_modes_e2e_python_files(test_query_modules_storage_modes.py)
|
||||
|
||||
add_subdirectory(query_modules)
|
||||
37
tests/e2e/query_modules_storage_modes/common.py
Normal file
37
tests/e2e/query_modules_storage_modes/common.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
import typing
|
||||
|
||||
import mgclient
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def cursor(**kwargs) -> mgclient.Connection:
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.execute("MATCH (n) DETACH DELETE n;")
|
||||
cursor.execute("CREATE (m:Component {id: 'A7422'}), (n:Component {id: '7X8X0'});")
|
||||
cursor.execute("MATCH (m:Component {id: 'A7422'}) MATCH (n:Component {id: '7X8X0'}) CREATE (m)-[:PART_OF]->(n);")
|
||||
cursor.execute("MATCH (m:Component {id: 'A7422'}) MATCH (n:Component {id: '7X8X0'}) CREATE (n)-[:DEPENDS_ON]->(m);")
|
||||
|
||||
yield cursor
|
||||
|
||||
cursor.execute("MATCH (n) DETACH DELETE n;")
|
||||
|
||||
|
||||
def connect(**kwargs):
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
return connection.cursor()
|
||||
@@ -0,0 +1,4 @@
|
||||
copy_qm_storage_modes_e2e_python_files(python_api.py)
|
||||
|
||||
add_query_module(c_api c_api.cpp)
|
||||
add_query_module(cpp_api cpp_api.cpp)
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2023 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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "_mgp.hpp"
|
||||
#include "mg_exceptions.hpp"
|
||||
#include "mg_procedure.h"
|
||||
|
||||
constexpr std::string_view kFunctionPassRelationship = "pass_relationship";
|
||||
constexpr std::string_view kPassRelationshipArg = "relationship";
|
||||
|
||||
constexpr std::string_view kProcedurePassNodeWithId = "pass_node_with_id";
|
||||
constexpr std::string_view kPassNodeWithIdArg = "node";
|
||||
constexpr std::string_view kPassNodeWithIdFieldNode = "node";
|
||||
constexpr std::string_view kPassNodeWithIdFieldId = "id";
|
||||
|
||||
// While the query procedure/function sleeps for this amount of time, a parallel transaction will erase a graph element
|
||||
// (node or relationship) contained in the return value. Any operation in the parallel transaction should take far less
|
||||
// time than this value.
|
||||
const int64_t kSleep = 1;
|
||||
|
||||
void PassRelationship(mgp_list *args, mgp_func_context *ctx, mgp_func_result *res, mgp_memory *memory) {
|
||||
auto *relationship = mgp::list_at(args, 0);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(kSleep));
|
||||
|
||||
mgp::func_result_set_value(res, relationship, memory);
|
||||
}
|
||||
|
||||
void PassNodeWithId(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
auto *node = mgp::value_get_vertex(mgp::list_at(args, 0));
|
||||
auto node_id = mgp::vertex_get_id(node).as_int;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(kSleep));
|
||||
|
||||
auto *result_record = mgp::result_new_record(result);
|
||||
mgp::result_record_insert(result_record, kPassNodeWithIdFieldNode.data(), mgp::value_make_vertex(node));
|
||||
mgp::result_record_insert(result_record, kPassNodeWithIdFieldId.data(), mgp::value_make_int(node_id, memory));
|
||||
}
|
||||
|
||||
extern "C" int mgp_init_module(struct mgp_module *query_module, struct mgp_memory *memory) {
|
||||
try {
|
||||
{
|
||||
auto *func = mgp::module_add_function(query_module, kFunctionPassRelationship.data(), PassRelationship);
|
||||
mgp::func_add_arg(func, kPassRelationshipArg.data(), mgp::type_relationship());
|
||||
}
|
||||
{
|
||||
auto *proc = mgp::module_add_read_procedure(query_module, kProcedurePassNodeWithId.data(), PassNodeWithId);
|
||||
mgp::proc_add_arg(proc, kPassNodeWithIdArg.data(), mgp::type_node());
|
||||
mgp::proc_add_result(proc, kPassNodeWithIdFieldNode.data(), mgp::type_node());
|
||||
mgp::proc_add_result(proc, kPassNodeWithIdFieldId.data(), mgp::type_int());
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int mgp_shutdown_module() { return 0; }
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2023 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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <mgp.hpp>
|
||||
|
||||
constexpr std::string_view kFunctionPassRelationship = "pass_relationship";
|
||||
constexpr std::string_view kPassRelationshipArg = "relationship";
|
||||
|
||||
constexpr std::string_view kProcedurePassNodeWithId = "pass_node_with_id";
|
||||
constexpr std::string_view kPassNodeWithIdArg = "node";
|
||||
constexpr std::string_view kPassNodeWithIdFieldNode = "node";
|
||||
constexpr std::string_view kPassNodeWithIdFieldId = "id";
|
||||
|
||||
// While the query procedure/function sleeps for this amount of time, a parallel transaction will erase a graph element
|
||||
// (node or relationship) contained in the return value. Any operation in the parallel transaction should take far less
|
||||
// time than this value.
|
||||
const int64_t kSleep = 1;
|
||||
|
||||
void PassRelationship(mgp_list *args, mgp_func_context *ctx, mgp_func_result *res, mgp_memory *memory) {
|
||||
try {
|
||||
mgp::MemoryDispatcherGuard guard{memory};
|
||||
const auto arguments = mgp::List(args);
|
||||
auto result = mgp::Result(res);
|
||||
|
||||
const auto relationship = arguments[0].ValueRelationship();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(kSleep));
|
||||
|
||||
result.SetValue(relationship);
|
||||
} catch (const std::exception &e) {
|
||||
mgp::func_result_set_error_msg(res, e.what(), memory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PassNodeWithId(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
try {
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
const auto arguments = mgp::List(args);
|
||||
const auto record_factory = mgp::RecordFactory(result);
|
||||
|
||||
const auto node = arguments[0].ValueNode();
|
||||
const auto node_id = node.Id().AsInt();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(kSleep));
|
||||
|
||||
auto record = record_factory.NewRecord();
|
||||
record.Insert(kPassNodeWithIdFieldNode.data(), node);
|
||||
record.Insert(kPassNodeWithIdFieldId.data(), node_id);
|
||||
} catch (const std::exception &e) {
|
||||
mgp::result_set_error_msg(result, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int mgp_init_module(struct mgp_module *query_module, struct mgp_memory *memory) {
|
||||
try {
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
mgp::AddFunction(PassRelationship, kFunctionPassRelationship,
|
||||
{mgp::Parameter(kPassRelationshipArg, mgp::Type::Relationship)}, query_module, memory);
|
||||
|
||||
mgp::AddProcedure(
|
||||
PassNodeWithId, kProcedurePassNodeWithId, mgp::ProcedureType::Read,
|
||||
{mgp::Parameter(kPassNodeWithIdArg, mgp::Type::Node)},
|
||||
{mgp::Return(kPassNodeWithIdFieldNode, mgp::Type::Node), mgp::Return(kPassNodeWithIdFieldId, mgp::Type::Int)},
|
||||
query_module, memory);
|
||||
} catch (const std::exception &e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int mgp_shutdown_module() { return 0; }
|
||||
@@ -0,0 +1,55 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
from time import sleep
|
||||
|
||||
import mgp
|
||||
|
||||
# While the query procedure/function sleeps for this amount of time, a parallel transaction will erase a graph element
|
||||
# (node or relationship) contained in the return value. Any operation in the parallel transaction should take far less
|
||||
# time than this value.
|
||||
SLEEP = 1
|
||||
|
||||
|
||||
@mgp.read_proc
|
||||
def pass_node_with_id(ctx: mgp.ProcCtx, node: mgp.Vertex) -> mgp.Record(node=mgp.Vertex, id=int):
|
||||
sleep(SLEEP)
|
||||
return mgp.Record(node=node, id=node.id)
|
||||
|
||||
|
||||
@mgp.function
|
||||
def pass_node(ctx: mgp.FuncCtx, node: mgp.Vertex):
|
||||
sleep(SLEEP)
|
||||
return node
|
||||
|
||||
|
||||
@mgp.function
|
||||
def pass_relationship(ctx: mgp.FuncCtx, relationship: mgp.Edge):
|
||||
sleep(SLEEP)
|
||||
return relationship
|
||||
|
||||
|
||||
@mgp.function
|
||||
def pass_path(ctx: mgp.FuncCtx, path: mgp.Path):
|
||||
sleep(SLEEP)
|
||||
return path
|
||||
|
||||
|
||||
@mgp.function
|
||||
def pass_list(ctx: mgp.FuncCtx, list_: mgp.List[mgp.Any]):
|
||||
sleep(SLEEP)
|
||||
return list_
|
||||
|
||||
|
||||
@mgp.function
|
||||
def pass_map(ctx: mgp.FuncCtx, map_: mgp.Map):
|
||||
sleep(SLEEP)
|
||||
return map_
|
||||
@@ -0,0 +1,283 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
# isort: off
|
||||
from multiprocessing import Process
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from common import cursor, connect
|
||||
|
||||
import time
|
||||
|
||||
SWITCH_TO_ANALYTICAL = "STORAGE MODE IN_MEMORY_ANALYTICAL;"
|
||||
|
||||
|
||||
def modify_graph(query):
|
||||
subprocess_cursor = connect()
|
||||
|
||||
time.sleep(0.5) # Time for the parallel transaction to call a query procedure
|
||||
|
||||
subprocess_cursor.execute(query)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api", ["c", "cpp", "python"])
|
||||
def test_function_delete_result(cursor, api):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (m:Component {id: 'A7422'})-[e:PART_OF]->(n:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(f"MATCH (m)-[e]->(n) RETURN {api}_api.pass_relationship(e);")
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0].type == "DEPENDS_ON"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api", ["c", "cpp", "python"])
|
||||
def test_function_delete_only_result(cursor, api):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
cursor.execute("MATCH (m:Component {id: '7X8X0'})-[e:DEPENDS_ON]->(n:Component {id: 'A7422'}) DELETE e;")
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (m:Component {id: 'A7422'})-[e:PART_OF]->(n:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(f"MATCH (m)-[e]->(n) RETURN {api}_api.pass_relationship(e);")
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api", ["c", "cpp", "python"])
|
||||
def test_procedure_delete_result(cursor, api):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (n {id: 'A7422'}) DETACH DELETE n;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
f"""MATCH (n)
|
||||
CALL {api}_api.pass_node_with_id(n)
|
||||
YIELD node, id
|
||||
RETURN node, id;"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 2 and result[0][0].properties["id"] == "7X8X0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api", ["c", "cpp", "python"])
|
||||
def test_procedure_delete_only_result(cursor, api):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
cursor.execute("MATCH (n {id: '7X8X0'}) DETACH DELETE n;")
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (n {id: 'A7422'}) DETACH DELETE n;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
f"""MATCH (n)
|
||||
CALL {api}_api.pass_node_with_id(n)
|
||||
YIELD node, id
|
||||
RETURN node, id;"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_deleted_node(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(target=modify_graph, args=("MATCH (n:Component {id: 'A7422'}) DETACH DELETE n;",))
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (n: Component {id: 'A7422'})
|
||||
RETURN python_api.pass_node(n);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
def test_deleted_relationship(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (:Component {id: 'A7422'})-[e:PART_OF]->(:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (:Component {id: 'A7422'})-[e:PART_OF]->(:Component {id: '7X8X0'})
|
||||
RETURN python_api.pass_relationship(e);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
def test_deleted_node_in_path(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(target=modify_graph, args=("MATCH (n:Component {id: 'A7422'}) DETACH DELETE n;",))
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH path=(n {id: 'A7422'})-[e]->(m)
|
||||
RETURN python_api.pass_path(path);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
def test_deleted_relationship_in_path(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (:Component {id: 'A7422'})-[e:PART_OF]->(:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH path=(n {id: 'A7422'})-[e]->(m)
|
||||
RETURN python_api.pass_path(path);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
def test_deleted_value_in_list(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (:Component {id: 'A7422'})-[e:PART_OF]->(:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (n)-[e]->()
|
||||
WITH collect(n) + collect(e) as list
|
||||
RETURN python_api.pass_list(list);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
def test_deleted_value_in_map(cursor):
|
||||
cursor.execute(SWITCH_TO_ANALYTICAL)
|
||||
|
||||
deleter = Process(
|
||||
target=modify_graph,
|
||||
args=("MATCH (:Component {id: 'A7422'})-[e:PART_OF]->(:Component {id: '7X8X0'}) DELETE e;",),
|
||||
)
|
||||
deleter.start()
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (n {id: 'A7422'})-[e]->()
|
||||
WITH {node: n, relationship: e} AS map
|
||||
RETURN python_api.pass_map(map);"""
|
||||
)
|
||||
|
||||
deleter.join()
|
||||
|
||||
result = cursor.fetchall()
|
||||
|
||||
assert len(result) == 1 and len(result[0]) == 1 and result[0][0] is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("storage_mode", ["IN_MEMORY_TRANSACTIONAL", "IN_MEMORY_ANALYTICAL"])
|
||||
def test_function_none_deleted(storage_mode):
|
||||
cursor = connect()
|
||||
|
||||
cursor.execute(f"STORAGE MODE {storage_mode};")
|
||||
cursor.execute("CREATE (m:Component {id: 'A7422'}), (n:Component {id: '7X8X0'});")
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (n)
|
||||
RETURN python_api.pass_node(n);"""
|
||||
)
|
||||
|
||||
result = cursor.fetchall()
|
||||
cursor.execute("MATCH (n) DETACH DELETE n;")
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("storage_mode", ["IN_MEMORY_TRANSACTIONAL", "IN_MEMORY_ANALYTICAL"])
|
||||
def test_procedure_none_deleted(storage_mode):
|
||||
cursor = connect()
|
||||
|
||||
cursor.execute(f"STORAGE MODE {storage_mode};")
|
||||
cursor.execute("CREATE (m:Component {id: 'A7422'}), (n:Component {id: '7X8X0'});")
|
||||
|
||||
cursor.execute(
|
||||
"""MATCH (n)
|
||||
CALL python_api.pass_node_with_id(n)
|
||||
YIELD node, id
|
||||
RETURN node, id;"""
|
||||
)
|
||||
|
||||
result = cursor.fetchall()
|
||||
cursor.execute("MATCH (n) DETACH DELETE n;")
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-rA"]))
|
||||
14
tests/e2e/query_modules_storage_modes/workloads.yaml
Normal file
14
tests/e2e/query_modules_storage_modes/workloads.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
query_modules_storage_modes_cluster: &query_modules_storage_modes_cluster
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||
log_file: "query_modules_storage_modes.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
|
||||
workloads:
|
||||
- name: "Test query module API behavior in Memgraph storage modes"
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
proc: "tests/e2e/query_modules_storage_modes/query_modules/"
|
||||
args: ["query_modules_storage_modes/test_query_modules_storage_modes.py"]
|
||||
<<: *query_modules_storage_modes_cluster
|
||||
@@ -104,7 +104,7 @@ class MemgraphRunner:
|
||||
memgraph_binary = os.path.join(self.build_directory, "memgraph")
|
||||
args_mg = [
|
||||
memgraph_binary,
|
||||
"--storage-properties-on-edges",
|
||||
"--storage-properties-on-edges=true",
|
||||
"--data-directory",
|
||||
self.data_directory.name,
|
||||
"--log-file",
|
||||
|
||||
@@ -38,7 +38,8 @@ struct CppApiTestFixture : public ::testing::Test {
|
||||
|
||||
mgp_graph CreateGraph(const memgraph::storage::View view = memgraph::storage::View::NEW) {
|
||||
// the execution context can be null as it shouldn't be used in these tests
|
||||
return mgp_graph{&CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get()};
|
||||
return mgp_graph{&CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get(),
|
||||
memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL};
|
||||
}
|
||||
|
||||
memgraph::query::DbAccessor &CreateDbAccessor(const memgraph::storage::IsolationLevel isolationLevel) {
|
||||
@@ -499,6 +500,7 @@ TYPED_TEST(CppApiTestFixture, TestValueOperatorLessThan) {
|
||||
ASSERT_THROW(list_test < map_test, mgp::ValueException);
|
||||
ASSERT_THROW(list_test < list_test, mgp::ValueException);
|
||||
}
|
||||
|
||||
TYPED_TEST(CppApiTestFixture, TestNumberEquality) {
|
||||
mgp::Value double_1{1.0};
|
||||
mgp::Value int_1{static_cast<int64_t>(1)};
|
||||
|
||||
@@ -249,7 +249,7 @@ TYPED_TEST(CypherType, VertexSatisfiesType) {
|
||||
auto vertex = dba.InsertVertex();
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
memgraph::utils::Allocator<mgp_vertex> alloc(memory.impl);
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr, dba.GetStorageMode()};
|
||||
auto *mgp_vertex_v =
|
||||
EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_vertex, alloc.new_object<mgp_vertex>(vertex, &graph));
|
||||
const memgraph::query::TypedValue tv_vertex(vertex);
|
||||
@@ -274,7 +274,7 @@ TYPED_TEST(CypherType, EdgeSatisfiesType) {
|
||||
auto edge = *dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
memgraph::utils::Allocator<mgp_edge> alloc(memory.impl);
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr, dba.GetStorageMode()};
|
||||
auto *mgp_edge_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_edge, alloc.new_object<mgp_edge>(edge, &graph));
|
||||
const memgraph::query::TypedValue tv_edge(edge);
|
||||
CheckSatisfiesTypesAndNullable(
|
||||
@@ -298,7 +298,7 @@ TYPED_TEST(CypherType, PathSatisfiesType) {
|
||||
auto edge = *dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
memgraph::utils::Allocator<mgp_path> alloc(memory.impl);
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::NEW, nullptr, dba.GetStorageMode()};
|
||||
auto *mgp_vertex_v = alloc.new_object<mgp_vertex>(v1, &graph);
|
||||
auto path = EXPECT_MGP_NO_ERROR(mgp_path *, mgp_path_make_with_start, mgp_vertex_v, &memory);
|
||||
ASSERT_TRUE(path);
|
||||
|
||||
@@ -132,7 +132,7 @@ TYPED_TEST(PyModule, PyVertex) {
|
||||
auto storage_dba = this->db->Access();
|
||||
memgraph::query::DbAccessor dba(storage_dba.get());
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr, dba.GetStorageMode()};
|
||||
auto *vertex = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto *vertex_value = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_vertex,
|
||||
@@ -182,7 +182,7 @@ TYPED_TEST(PyModule, PyEdge) {
|
||||
auto storage_dba = this->db->Access();
|
||||
memgraph::query::DbAccessor dba(storage_dba.get());
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr, dba.GetStorageMode()};
|
||||
auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
|
||||
ASSERT_TRUE(start_v);
|
||||
auto *edges_it = EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, start_v, &memory);
|
||||
@@ -228,7 +228,7 @@ TYPED_TEST(PyModule, PyPath) {
|
||||
auto storage_dba = this->db->Access();
|
||||
memgraph::query::DbAccessor dba(storage_dba.get());
|
||||
mgp_memory memory{memgraph::utils::NewDeleteResource()};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr};
|
||||
mgp_graph graph{&dba, memgraph::storage::View::OLD, nullptr, dba.GetStorageMode()};
|
||||
auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
|
||||
ASSERT_TRUE(start_v);
|
||||
auto *path = EXPECT_MGP_NO_ERROR(mgp_path *, mgp_path_make_with_start, start_v, &memory);
|
||||
|
||||
@@ -120,7 +120,8 @@ class MgpGraphTest : public ::testing::Test {
|
||||
public:
|
||||
mgp_graph CreateGraph(const memgraph::storage::View view = memgraph::storage::View::NEW) {
|
||||
// the execution context can be null as it shouldn't be used in these tests
|
||||
return mgp_graph{&CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get()};
|
||||
return mgp_graph{&CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get(),
|
||||
memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL};
|
||||
}
|
||||
|
||||
std::array<memgraph::storage::Gid, 2> CreateEdge() {
|
||||
|
||||
Reference in New Issue
Block a user