Compare commits
7 Commits
use-extent
...
fix-kafka-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5370fd0f6 | ||
|
|
307f7e1827 | ||
|
|
1ad87927b7 | ||
|
|
0a39b6d838 | ||
|
|
cec19c4b54 | ||
|
|
0d5e8cd9d8 | ||
|
|
2a404c81a0 |
20
.github/workflows/daily_benchmark.yaml
vendored
20
.github/workflows/daily_benchmark.yaml
vendored
@@ -67,11 +67,7 @@ jobs:
|
||||
- name: Run mgbench
|
||||
run: |
|
||||
cd tests/mgbench
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 12 --export-results benchmark_pokec.json pokec/medium/*/*
|
||||
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_supernode.json supernode
|
||||
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_high_write_set_property.json high_write_set_property
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 12 --export-results benchmark_result.json pokec/medium/*/*
|
||||
|
||||
- name: Upload mgbench results
|
||||
run: |
|
||||
@@ -80,19 +76,7 @@ jobs:
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "mgbench" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_pokec.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
./main.py --benchmark-name "supernode" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_supernode.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
./main.py --benchmark-name "high_write_set_property" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_high_write_set_property.json" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_result.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
14
.github/workflows/diff.yaml
vendored
14
.github/workflows/diff.yaml
vendored
@@ -101,7 +101,7 @@ jobs:
|
||||
echo ${file}
|
||||
if [[ ${file} == *.py ]]; then
|
||||
python3 -m black --check --diff ${file}
|
||||
python3 -m isort --profile black --check-only --diff ${file}
|
||||
python3 -m isort --check-only --diff ${file}
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -229,11 +229,6 @@ jobs:
|
||||
# branches and tags. (default: 1)
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check e2e service dependencies
|
||||
run: |
|
||||
cd tests/e2e
|
||||
./dependency_check.sh
|
||||
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
@@ -280,13 +275,11 @@ jobs:
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
source ve3/bin/activate
|
||||
./continuous_integration
|
||||
|
||||
- name: Run stress test (SSL)
|
||||
run: |
|
||||
cd tests/stress
|
||||
source ve3/bin/activate
|
||||
./continuous_integration --use-ssl
|
||||
|
||||
- name: Run durability test
|
||||
@@ -417,12 +410,11 @@ jobs:
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "macro_benchmark" \
|
||||
--benchmark-results "../../tests/macro_benchmark/.harness_summary" \
|
||||
--benchmark-results-path "../../tests/macro_benchmark/.harness_summary" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
# TODO (andi) No need for path flags and for --disk-storage and --in-memory-analytical
|
||||
- name: Run mgbench
|
||||
run: |
|
||||
cd tests/mgbench
|
||||
@@ -435,7 +427,7 @@ jobs:
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "mgbench" \
|
||||
--benchmark-results "../../tests/mgbench/benchmark_result.json" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_result.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
77
.github/workflows/performance_benchmarks.yaml
vendored
77
.github/workflows/performance_benchmarks.yaml
vendored
@@ -1,77 +0,0 @@
|
||||
name: Run performance benchmarks manually
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
performance_benchmarks:
|
||||
name: "Performance benchmarks"
|
||||
runs-on: [self-hosted, Linux, X64, Diff, Gen7]
|
||||
env:
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
|
||||
# Initialize dependencies.
|
||||
./init
|
||||
|
||||
# Build only memgraph release binaries.
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j$THREADS
|
||||
|
||||
- name: Get branch name (merge)
|
||||
if: github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV
|
||||
|
||||
- name: Get branch name (pull request)
|
||||
if: github.event_name == 'pull_request'
|
||||
shell: bash
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF} | tr / -)" >> $GITHUB_ENV
|
||||
|
||||
- name: Run benchmarks
|
||||
run: |
|
||||
cd tests/mgbench
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 12 --export-results benchmark_result.json pokec/medium/*/*
|
||||
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_supernode.json supernode
|
||||
|
||||
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_high_write_set_property.json high_write_set_property
|
||||
|
||||
- name: Upload benchmark results
|
||||
run: |
|
||||
cd tools/bench-graph-client
|
||||
virtualenv -p python3 ve3
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "mgbench" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_result.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
./main.py --benchmark-name "supernode" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_supernode.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
./main.py --benchmark-name "high_write_set_property" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_high_write_set_property.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,7 +16,8 @@
|
||||
.ycm_extra_conf.pyc
|
||||
.temp/
|
||||
Testing/
|
||||
/build*/
|
||||
build
|
||||
build/
|
||||
release/examples/build
|
||||
cmake-build-*
|
||||
cmake/DownloadProject/
|
||||
|
||||
@@ -15,7 +15,6 @@ repos:
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
args: ["--profile", "black"]
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v13.0.0
|
||||
hooks:
|
||||
|
||||
55
cmake/FindJemalloc.cmake
Normal file
55
cmake/FindJemalloc.cmake
Normal file
@@ -0,0 +1,55 @@
|
||||
# Try to find jemalloc library
|
||||
#
|
||||
# Use this module as:
|
||||
# find_package(Jemalloc)
|
||||
#
|
||||
# or:
|
||||
# find_package(Jemalloc REQUIRED)
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# Jemalloc_FOUND True if the system has the jemalloc library.
|
||||
# Jemalloc_INCLUDE_DIRS Include directories needed to use jemalloc.
|
||||
# Jemalloc_LIBRARIES Libraries needed to link to jemalloc.
|
||||
#
|
||||
# The following cache variables may also be set:
|
||||
#
|
||||
# Jemalloc_INCLUDE_DIR The directory containing jemalloc/jemalloc.h.
|
||||
# Jemalloc_LIBRARY The path to the jemalloc static library.
|
||||
|
||||
find_path(Jemalloc_INCLUDE_DIR NAMES jemalloc/jemalloc.h PATH_SUFFIXES include)
|
||||
|
||||
find_library(Jemalloc_LIBRARY NAMES libjemalloc.a PATH_SUFFIXES lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Jemalloc
|
||||
FOUND_VAR Jemalloc_FOUND
|
||||
REQUIRED_VARS
|
||||
Jemalloc_LIBRARY
|
||||
Jemalloc_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(Jemalloc_FOUND)
|
||||
set(Jemalloc_LIBRARIES ${Jemalloc_LIBRARY})
|
||||
set(Jemalloc_INCLUDE_DIRS ${Jemalloc_INCLUDE_DIR})
|
||||
else()
|
||||
if(Jemalloc_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Cannot find jemalloc!")
|
||||
else()
|
||||
message(WARNING "jemalloc is not found!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(Jemalloc_FOUND AND NOT TARGET Jemalloc::Jemalloc)
|
||||
add_library(Jemalloc::Jemalloc UNKNOWN IMPORTED)
|
||||
set_target_properties(Jemalloc::Jemalloc
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${Jemalloc_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Jemalloc_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
Jemalloc_INCLUDE_DIR
|
||||
Jemalloc_LIBRARY
|
||||
)
|
||||
@@ -1,67 +0,0 @@
|
||||
# Try to find jemalloc library
|
||||
#
|
||||
# Use this module as:
|
||||
# find_package(jemalloc)
|
||||
#
|
||||
# or:
|
||||
# find_package(jemalloc REQUIRED)
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# JEMALLOC_FOUND True if the system has the jemalloc library.
|
||||
# Jemalloc_INCLUDE_DIRS Include directories needed to use jemalloc.
|
||||
# Jemalloc_LIBRARIES Libraries needed to link to jemalloc.
|
||||
#
|
||||
# The following cache variables may also be set:
|
||||
#
|
||||
# Jemalloc_INCLUDE_DIR The directory containing jemalloc/jemalloc.h.
|
||||
# Jemalloc_LIBRARY The path to the jemalloc static library.
|
||||
|
||||
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(jemalloc
|
||||
FOUND_VAR JEMALLOC_FOUND
|
||||
REQUIRED_VARS
|
||||
JEMALLOC_LIBRARY
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(JEMALLOC_INCLUDE_DIR)
|
||||
message(STATUS "Found jemalloc include dir: ${JEMALLOC_INCLUDE_DIR}")
|
||||
else()
|
||||
message(WARNING "jemalloc not found!")
|
||||
endif()
|
||||
|
||||
if(JEMALLOC_LIBRARY)
|
||||
message(STATUS "Found jemalloc library: ${JEMALLOC_LIBRARY}")
|
||||
else()
|
||||
message(WARNING "jemalloc library not found!")
|
||||
endif()
|
||||
|
||||
if(JEMALLOC_FOUND)
|
||||
set(Jemalloc_LIBRARIES ${JEMALLOC_LIBRARY})
|
||||
set(Jemalloc_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR})
|
||||
else()
|
||||
if(Jemalloc_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Cannot find jemalloc!")
|
||||
else()
|
||||
message(WARNING "jemalloc is not found!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(JEMALLOC_FOUND AND NOT TARGET Jemalloc::Jemalloc)
|
||||
message(STATUS "JEMALLOC NOT TARGET")
|
||||
|
||||
add_library(Jemalloc::Jemalloc UNKNOWN IMPORTED)
|
||||
set_target_properties(Jemalloc::Jemalloc
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${JEMALLOC_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${JEMALLOC_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
JEMALLOC_LIBRARY
|
||||
)
|
||||
@@ -99,10 +99,6 @@ modifications:
|
||||
value: "SNAPSHOT_ISOLATION"
|
||||
override: true
|
||||
|
||||
- name: "storage_mode"
|
||||
value: "IN_MEMORY_TRANSACTIONAL"
|
||||
override: true
|
||||
|
||||
- name: "allow_load_csv"
|
||||
value: "true"
|
||||
override: false
|
||||
@@ -111,10 +107,6 @@ modifications:
|
||||
value: "false"
|
||||
override: true
|
||||
|
||||
- name: "query_callable_mappings_path"
|
||||
value: "/etc/memgraph/apoc_compatibility_mappings.json"
|
||||
override: true
|
||||
|
||||
undocumented:
|
||||
- "flag_file"
|
||||
- "also_log_to_stderr"
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"dbms.components": "mgps.components",
|
||||
"apoc.util.validate": "mgps.validate",
|
||||
"db.schema.nodeTypeProperties":"schema.node_type_properties",
|
||||
"db.schema.relTypeProperties":"schema.rel_type_properties",
|
||||
"apoc.coll.contains": "collections.contains",
|
||||
"apoc.coll.partition": "collections.partition",
|
||||
"apoc.coll.toSet": "collections.to_set",
|
||||
"apoc.coll.unionAll": "collections.unionAll",
|
||||
"apoc.coll.removeAll": "collections.remove_all",
|
||||
"apoc.coll.union": "collections.union",
|
||||
"apoc.coll.sum": "collections.sum",
|
||||
"apoc.coll.pairs": "collections.pairs",
|
||||
"apoc.map.fromLists": "map.from_lists",
|
||||
"apoc.map.removeKeys": "map.remove_keys",
|
||||
"apoc.map.merge": "map.merge",
|
||||
"apoc.create.nodes": "create.nodes",
|
||||
"apoc.create.removeProperties": "create.remove_properties",
|
||||
"apoc.create.node": "create.node",
|
||||
"apoc.create.removeLabel": "create.remove_label",
|
||||
"apoc.refactor.invert": "refactor.invert",
|
||||
"apoc.refactor.cloneNode": "refactor.clone_node",
|
||||
"apoc.refactor.cloneSubgraph": "refactor.clone_subgraph",
|
||||
"apoc.refactor.cloneSubgraphFromPath": "refactor.clone_subgraph_from_path",
|
||||
"apoc.label.exists": "label.exists"
|
||||
|
||||
}
|
||||
@@ -255,16 +255,6 @@ inline mgp_edge *graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_verte
|
||||
return MgInvoke<mgp_edge *>(mgp_graph_create_edge, graph, from, to, type, memory);
|
||||
}
|
||||
|
||||
inline mgp_edge *graph_edge_set_from(struct mgp_graph *graph, struct mgp_edge *e, struct mgp_vertex *new_from,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_edge *>(mgp_graph_edge_set_from, graph, e, new_from, memory);
|
||||
}
|
||||
|
||||
inline mgp_edge *graph_edge_set_to(struct mgp_graph *graph, struct mgp_edge *e, struct mgp_vertex *new_to,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_edge *>(mgp_graph_edge_set_to, graph, e, new_to, memory);
|
||||
}
|
||||
|
||||
inline void graph_delete_edge(mgp_graph *graph, mgp_edge *edge) { MgInvokeVoid(mgp_graph_delete_edge, graph, edge); }
|
||||
|
||||
inline mgp_vertex *graph_get_vertex_by_id(mgp_graph *g, mgp_vertex_id id, mgp_memory *memory) {
|
||||
@@ -377,10 +367,6 @@ inline mgp_map_item *map_items_iterator_next(mgp_map_items_iterator *it) {
|
||||
|
||||
inline mgp_vertex_id vertex_get_id(mgp_vertex *v) { return MgInvoke<mgp_vertex_id>(mgp_vertex_get_id, v); }
|
||||
|
||||
inline size_t vertex_get_in_degree(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_get_in_degree, v); }
|
||||
|
||||
inline size_t vertex_get_out_degree(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_get_out_degree, v); }
|
||||
|
||||
inline mgp_vertex *vertex_copy(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertex *>(mgp_vertex_copy, v, memory);
|
||||
}
|
||||
@@ -415,10 +401,6 @@ inline void vertex_set_property(mgp_vertex *v, const char *property_name, mgp_va
|
||||
MgInvokeVoid(mgp_vertex_set_property, v, property_name, property_value);
|
||||
}
|
||||
|
||||
inline void vertex_set_properties(mgp_vertex *v, struct mgp_map *properties) {
|
||||
MgInvokeVoid(mgp_vertex_set_properties, v, properties);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *vertex_iter_properties(mgp_vertex *v, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_vertex_iter_properties, v, memory);
|
||||
}
|
||||
@@ -455,10 +437,6 @@ inline void edge_set_property(mgp_edge *e, const char *property_name, mgp_value
|
||||
MgInvokeVoid(mgp_edge_set_property, e, property_name, property_value);
|
||||
}
|
||||
|
||||
inline void edge_set_properties(mgp_edge *e, struct mgp_map *properties) {
|
||||
MgInvokeVoid(mgp_edge_set_properties, e, properties);
|
||||
}
|
||||
|
||||
inline mgp_properties_iterator *edge_iter_properties(mgp_edge *e, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_properties_iterator *>(mgp_edge_iter_properties, e, memory);
|
||||
}
|
||||
@@ -477,8 +455,6 @@ inline void path_destroy(mgp_path *path) { mgp_path_destroy(path); }
|
||||
|
||||
inline void path_expand(mgp_path *path, mgp_edge *edge) { MgInvokeVoid(mgp_path_expand, path, edge); }
|
||||
|
||||
inline void path_pop(mgp_path *path) { MgInvokeVoid(mgp_path_pop, path); }
|
||||
|
||||
inline size_t path_size(mgp_path *path) { return MgInvoke<size_t>(mgp_path_size, path); }
|
||||
|
||||
inline mgp_vertex *path_vertex_at(mgp_path *path, size_t index) {
|
||||
|
||||
@@ -333,13 +333,6 @@ class Path:
|
||||
self._vertices.append(edge.end_id)
|
||||
self._edges.append((edge.start_id, edge.end_id, edge.id))
|
||||
|
||||
def pop(self):
|
||||
if not self._edges:
|
||||
raise IndexError("Path contains no relationships.")
|
||||
|
||||
self._vertices.pop()
|
||||
self._edges.pop()
|
||||
|
||||
def vertex_at(self, index: int) -> Vertex:
|
||||
return Vertex(self._vertices[index], self._graph)
|
||||
|
||||
|
||||
@@ -111,35 +111,6 @@ enum mgp_error mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment,
|
||||
/// The behavior is undefined if `ptr` is not a value returned from a prior
|
||||
/// mgp_global_alloc() or mgp_global_aligned_alloc().
|
||||
void mgp_global_free(void *p);
|
||||
|
||||
/// State of the graph database.
|
||||
struct mgp_graph;
|
||||
|
||||
/// Allocations are tracked only for master thread. If new threads are spawned
|
||||
/// inside procedure, by calling following function with thread id
|
||||
/// you can start tracking allocations for that thread too. This
|
||||
/// is important if you need query memory limit to work
|
||||
/// for given procedure or per procedure memory limit.
|
||||
enum mgp_error mgp_track_thread_allocations(struct mgp_graph *graph, const char *thread_id);
|
||||
|
||||
/// Once allocations are tracked for custom thread, you need to stop tracking allocations
|
||||
/// for given thread, before thread finishes with execution, or is detached.
|
||||
/// Otherwise it might result in slowdown of system due to unnecessary tracking of
|
||||
/// allocations.
|
||||
enum mgp_error mgp_untrack_thread_allocations(struct mgp_graph *graph, const char *thread_id);
|
||||
|
||||
/// Allocations are tracked only for master thread. If new threads are spawned
|
||||
/// inside procedure, by calling following function
|
||||
/// you can start tracking allocations for current thread too. This
|
||||
/// is important if you need query memory limit to work
|
||||
/// for given procedure or per procedure memory limit.
|
||||
enum mgp_error mgp_track_current_thread_allocations(struct mgp_graph *graph);
|
||||
|
||||
/// Once allocations are tracked for current thread, you need to stop tracking allocations
|
||||
/// for given thread, before thread finishes with execution, or is detached.
|
||||
/// Otherwise it might result in slowdown of system due to unnecessary tracking of
|
||||
/// allocations.
|
||||
enum mgp_error mgp_untrack_current_thread_allocations(struct mgp_graph *graph);
|
||||
///@}
|
||||
|
||||
/// @name Operations on mgp_value
|
||||
@@ -572,10 +543,6 @@ void mgp_path_destroy(struct mgp_path *path);
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for path extension.
|
||||
enum mgp_error mgp_path_expand(struct mgp_path *path, struct mgp_edge *edge);
|
||||
|
||||
/// Remove the last node and the last relationship from the path.
|
||||
/// Return mgp_error::MGP_ERROR_OUT_OF_RANGE if the path contains no relationships.
|
||||
enum mgp_error mgp_path_pop(struct mgp_path *path);
|
||||
|
||||
/// Get the number of edges in a mgp_path.
|
||||
/// Current implementation always returns without errors.
|
||||
enum mgp_error mgp_path_size(struct mgp_path *path, size_t *result);
|
||||
@@ -680,12 +647,6 @@ struct mgp_vertex_id {
|
||||
/// Get the ID of given vertex.
|
||||
enum mgp_error mgp_vertex_get_id(struct mgp_vertex *v, struct mgp_vertex_id *result);
|
||||
|
||||
/// Get the in degree of given vertex.
|
||||
enum mgp_error mgp_vertex_get_in_degree(struct mgp_vertex *v, size_t *result);
|
||||
|
||||
/// Get the out degree of given vertex.
|
||||
enum mgp_error mgp_vertex_get_out_degree(struct mgp_vertex *v, size_t *result);
|
||||
|
||||
/// Result is non-zero if the vertex can be modified.
|
||||
/// The mutability of the vertex is the same as the graph which it is part of. If a vertex is immutable, then edges
|
||||
/// cannot be created or deleted, properties and labels cannot be set or removed and all of the returned edges will be
|
||||
@@ -703,15 +664,6 @@ enum mgp_error mgp_vertex_underlying_graph_is_mutable(struct mgp_vertex *v, int
|
||||
enum mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_name,
|
||||
struct mgp_value *property_value);
|
||||
|
||||
/// Set the value of properties on a vertex.
|
||||
/// When the value is `null`, then the property is removed from the vertex.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the property.
|
||||
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `v` is immutable.
|
||||
/// Return mgp_error::MGP_ERROR_DELETED_OBJECT if `v` has been deleted.
|
||||
/// Return mgp_error::MGP_ERROR_SERIALIZATION_ERROR if `v` has been modified by another transaction.
|
||||
/// Return mgp_error::MGP_ERROR_VALUE_CONVERSION if `property_value` is vertex, edge or path.
|
||||
enum mgp_error mgp_vertex_set_properties(struct mgp_vertex *v, struct mgp_map *properties);
|
||||
|
||||
/// Add the label to the vertex.
|
||||
/// If the vertex already has the label, this function does nothing.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the label.
|
||||
@@ -862,15 +814,6 @@ enum mgp_error mgp_edge_get_property(struct mgp_edge *e, const char *property_na
|
||||
/// Return mgp_error::MGP_ERROR_VALUE_CONVERSION if `property_value` is vertex, edge or path.
|
||||
enum mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, struct mgp_value *property_value);
|
||||
|
||||
/// Set the value of properties on a vertex.
|
||||
/// When the value is `null`, then the property is removed from the vertex.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the property.
|
||||
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `v` is immutable.
|
||||
/// Return mgp_error::MGP_ERROR_DELETED_OBJECT if `v` has been deleted.
|
||||
/// Return mgp_error::MGP_ERROR_SERIALIZATION_ERROR if `v` has been modified by another transaction.
|
||||
/// Return mgp_error::MGP_ERROR_VALUE_CONVERSION if `property_value` is vertex, edge or path.
|
||||
enum mgp_error mgp_edge_set_properties(struct mgp_edge *e, struct mgp_map *properties);
|
||||
|
||||
/// Start iterating over properties stored in the given edge.
|
||||
/// The properties of the edge are copied when the iterator is created, therefore later changes won't affect them.
|
||||
/// Resulting mgp_properties_iterator needs to be deallocated with
|
||||
@@ -880,6 +823,9 @@ enum mgp_error mgp_edge_set_properties(struct mgp_edge *e, struct mgp_map *prope
|
||||
enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *memory,
|
||||
struct mgp_properties_iterator **result);
|
||||
|
||||
/// State of the graph database.
|
||||
struct mgp_graph;
|
||||
|
||||
/// Get the vertex corresponding to given ID, or NULL if no such vertex exists.
|
||||
/// Resulting vertex must be freed using mgp_vertex_destroy.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the vertex.
|
||||
@@ -918,22 +864,6 @@ enum mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, struct mg
|
||||
enum mgp_error mgp_graph_create_edge(struct mgp_graph *graph, struct mgp_vertex *from, struct mgp_vertex *to,
|
||||
struct mgp_edge_type type, struct mgp_memory *memory, struct mgp_edge **result);
|
||||
|
||||
/// Change edge from vertex
|
||||
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
|
||||
/// Return mgp_error::MGP_ERROR_DELETED_OBJECT if `from` or `to` has been deleted.
|
||||
/// Return mgp_error::MGP_ERROR_SERIALIZATION_ERROR if `from` or `to` has been modified by another transaction.
|
||||
enum mgp_error mgp_graph_edge_set_from(struct mgp_graph *graph, struct mgp_edge *e, struct mgp_vertex *new_from,
|
||||
struct mgp_memory *memory, struct mgp_edge **result);
|
||||
|
||||
/// Change edge to vertex
|
||||
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
|
||||
/// Return mgp_error::MGP_ERROR_DELETED_OBJECT if `from` or `to` has been deleted.
|
||||
/// Return mgp_error::MGP_ERROR_SERIALIZATION_ERROR if `from` or `to` has been modified by another transaction.
|
||||
enum mgp_error mgp_graph_edge_set_to(struct mgp_graph *graph, struct mgp_edge *e, struct mgp_vertex *new_to,
|
||||
struct mgp_memory *memory, struct mgp_edge **result);
|
||||
|
||||
/// Delete an edge from the graph.
|
||||
/// Return mgp_error::MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
|
||||
/// Return mgp_error::MGP_ERROR_SERIALIZATION_ERROR if `edge`, its source or destination vertex has been modified by
|
||||
|
||||
288
include/mgp.hpp
288
include/mgp.hpp
@@ -126,12 +126,6 @@ class MemoryDispatcher final {
|
||||
map_.erase(this_id);
|
||||
}
|
||||
|
||||
bool IsThisThreadRegistered() noexcept {
|
||||
const auto this_id = std::this_thread::get_id();
|
||||
std::shared_lock lock(mut_);
|
||||
return map_.contains(this_id);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::thread::id, mgp_memory *> map_;
|
||||
std::shared_mutex mut_;
|
||||
@@ -142,7 +136,7 @@ class MemoryDispatcher final {
|
||||
// header. The use of the 'mgp_memory *memory' pointer is deprecated
|
||||
// and will be removed in upcoming releases.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline MemoryDispatcher mrd{};
|
||||
inline extern MemoryDispatcher mrd{};
|
||||
|
||||
// TODO - Once we deprecate this we should remove this
|
||||
// and make sure nothing relies on it anymore. This alone
|
||||
@@ -170,7 +164,7 @@ class MemoryDispatcherGuard final {
|
||||
// the mapping instead.
|
||||
template <typename Func, typename... Args>
|
||||
inline decltype(auto) MemHandlerCallback(Func &&func, Args &&...args) {
|
||||
if (!mrd.IsThisThreadRegistered()) {
|
||||
if (memory) {
|
||||
return std::forward<Func>(func)(std::forward<Args>(args)..., memory);
|
||||
}
|
||||
return std::forward<Func>(func)(std::forward<Args>(args)..., mrd.GetMemoryResource());
|
||||
@@ -256,10 +250,6 @@ class Graph {
|
||||
void DetachDeleteNode(const Node &node);
|
||||
/// @brief Creates a relationship of type `type` between nodes `from` and `to` and adds it to the graph.
|
||||
Relationship CreateRelationship(const Node &from, const Node &to, const std::string_view type);
|
||||
/// @brief Changes a relationship from node.
|
||||
void SetFrom(Relationship &relationship, const Node &new_from);
|
||||
/// @brief Changes a relationship to node.
|
||||
void SetTo(Relationship &relationship, const Node &new_to);
|
||||
/// @brief Deletes a relationship from the graph.
|
||||
void DeleteRelationship(const Relationship &relationship);
|
||||
|
||||
@@ -439,12 +429,6 @@ class Labels {
|
||||
friend class Labels;
|
||||
|
||||
public:
|
||||
using value_type = Labels;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const Labels *;
|
||||
using reference = const Labels &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
bool operator==(const Iterator &other) const;
|
||||
|
||||
bool operator!=(const Iterator &other) const;
|
||||
@@ -528,12 +512,6 @@ class List {
|
||||
friend class List;
|
||||
|
||||
public:
|
||||
using value_type = List;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const List *;
|
||||
using reference = const List &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
bool operator==(const Iterator &other) const;
|
||||
|
||||
bool operator!=(const Iterator &other) const;
|
||||
@@ -576,9 +554,6 @@ class List {
|
||||
/// @exception std::runtime_error List contains value of unknown type.
|
||||
bool operator!=(const List &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_list *ptr_;
|
||||
};
|
||||
@@ -694,9 +669,6 @@ class Map {
|
||||
/// @exception std::runtime_error Map contains value of unknown type.
|
||||
bool operator!=(const Map &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_map *ptr_;
|
||||
};
|
||||
@@ -737,14 +709,11 @@ class Node {
|
||||
bool HasLabel(std::string_view label) const;
|
||||
|
||||
/// @brief Returns an std::map of the node’s properties.
|
||||
std::unordered_map<std::string, Value> Properties() const;
|
||||
std::map<std::string, Value> Properties() const;
|
||||
|
||||
/// @brief Sets the chosen property to the given value.
|
||||
void SetProperty(std::string property, Value value);
|
||||
|
||||
/// @brief Sets the chosen properties to the given values.
|
||||
void SetProperties(std::unordered_map<std::string_view, Value> properties);
|
||||
|
||||
/// @brief Removes the chosen property.
|
||||
void RemoveProperty(std::string property);
|
||||
|
||||
@@ -771,15 +740,6 @@ class Node {
|
||||
/// @exception std::runtime_error Node properties contain value(s) of unknown type.
|
||||
bool operator!=(const Node &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
/// @brief returns the in degree of a node
|
||||
inline size_t InDegree() const;
|
||||
|
||||
/// @brief returns the out degree of a node
|
||||
inline size_t OutDegree() const;
|
||||
|
||||
private:
|
||||
mgp_vertex *ptr_;
|
||||
};
|
||||
@@ -815,14 +775,11 @@ class Relationship {
|
||||
std::string_view Type() const;
|
||||
|
||||
/// @brief Returns an std::map of the relationship’s properties.
|
||||
std::unordered_map<std::string, Value> Properties() const;
|
||||
std::map<std::string, Value> Properties() const;
|
||||
|
||||
/// @brief Sets the chosen property to the given value.
|
||||
void SetProperty(std::string property, Value value);
|
||||
|
||||
/// @brief Sets the chosen properties to the given values.
|
||||
void SetProperties(std::unordered_map<std::string_view, Value> properties);
|
||||
|
||||
/// @brief Removes the chosen property.
|
||||
void RemoveProperty(std::string property);
|
||||
|
||||
@@ -841,9 +798,6 @@ class Relationship {
|
||||
/// @exception std::runtime_error Relationship properties contain value(s) of unknown type.
|
||||
bool operator!=(const Relationship &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_edge *ptr_;
|
||||
};
|
||||
@@ -886,17 +840,12 @@ class Path {
|
||||
|
||||
/// @brief Adds a relationship continuing from the last node on the path.
|
||||
void Expand(const Relationship &relationship);
|
||||
/// @brief Removes the last node and the last relationship from the path.
|
||||
void Pop();
|
||||
|
||||
/// @exception std::runtime_error Path contains element(s) with unknown value.
|
||||
bool operator==(const Path &other) const;
|
||||
/// @exception std::runtime_error Path contains element(s) with unknown value.
|
||||
bool operator!=(const Path &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_path *ptr_;
|
||||
};
|
||||
@@ -954,9 +903,6 @@ class Date {
|
||||
|
||||
bool operator<(const Date &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_date *ptr_;
|
||||
};
|
||||
@@ -1016,9 +962,6 @@ class LocalTime {
|
||||
|
||||
bool operator<(const LocalTime &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_local_time *ptr_;
|
||||
};
|
||||
@@ -1084,9 +1027,6 @@ class LocalDateTime {
|
||||
|
||||
bool operator<(const LocalDateTime &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_local_date_time *ptr_;
|
||||
};
|
||||
@@ -1138,9 +1078,6 @@ class Duration {
|
||||
|
||||
bool operator<(const Duration &other) const;
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_duration *ptr_;
|
||||
};
|
||||
@@ -1351,9 +1288,6 @@ class Value {
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const mgp::Value &value);
|
||||
|
||||
/// @brief returns the string representation
|
||||
const std::string ToString() const;
|
||||
|
||||
private:
|
||||
mgp_value *ptr_;
|
||||
};
|
||||
@@ -2015,18 +1949,6 @@ inline Relationship Graph::CreateRelationship(const Node &from, const Node &to,
|
||||
return relationship;
|
||||
}
|
||||
|
||||
inline void Graph::SetFrom(Relationship &relationship, const Node &new_from) {
|
||||
mgp_edge *edge = mgp::MemHandlerCallback(mgp::graph_edge_set_from, graph_, relationship.ptr_, new_from.ptr_);
|
||||
relationship = Relationship(edge);
|
||||
mgp::edge_destroy(edge);
|
||||
}
|
||||
|
||||
inline void Graph::SetTo(Relationship &relationship, const Node &new_to) {
|
||||
mgp_edge *edge = mgp::MemHandlerCallback(mgp::graph_edge_set_to, graph_, relationship.ptr_, new_to.ptr_);
|
||||
relationship = Relationship(edge);
|
||||
mgp::edge_destroy(edge);
|
||||
}
|
||||
|
||||
inline void Graph::DeleteRelationship(const Relationship &relationship) {
|
||||
mgp::graph_delete_edge(graph_, relationship.ptr_);
|
||||
}
|
||||
@@ -2478,22 +2400,6 @@ inline bool List::operator==(const List &other) const { return util::ListsEqual(
|
||||
|
||||
inline bool List::operator!=(const List &other) const { return !(*this == other); }
|
||||
|
||||
inline const std::string List::ToString() const {
|
||||
const size_t size = Size();
|
||||
if (size == 0) {
|
||||
return "[]";
|
||||
}
|
||||
std::string return_str{"["};
|
||||
size_t i = 0;
|
||||
const mgp::List &list = (*this);
|
||||
while (i < size - 1) {
|
||||
return_str.append(list[i].ToString() + ", ");
|
||||
i++;
|
||||
}
|
||||
return_str.append(list[i].ToString() + "]");
|
||||
return return_str;
|
||||
}
|
||||
|
||||
// MapItem:
|
||||
|
||||
inline bool MapItem::operator==(MapItem &other) const { return key == other.key && value == other.value; }
|
||||
@@ -2663,24 +2569,6 @@ inline bool Map::operator==(const Map &other) const { return util::MapsEqual(ptr
|
||||
|
||||
inline bool Map::operator!=(const Map &other) const { return !(*this == other); }
|
||||
|
||||
inline const std::string Map::ToString() const {
|
||||
const size_t map_size = Size();
|
||||
if (map_size == 0) {
|
||||
return "{}";
|
||||
}
|
||||
std::string return_string{"{"};
|
||||
size_t i = 0;
|
||||
for (const auto &[key, value] : *this) {
|
||||
if (i == map_size - 1) {
|
||||
return_string.append(std::string(key) + ": " + value.ToString() + "}");
|
||||
break;
|
||||
}
|
||||
return_string.append(std::string(key) + ": " + value.ToString() + ", ");
|
||||
++i;
|
||||
}
|
||||
return return_string;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Graph elements (Node, Relationship & Path) */
|
||||
@@ -2758,9 +2646,9 @@ inline void Node::RemoveLabel(const std::string_view label) {
|
||||
mgp::vertex_remove_label(this->ptr_, mgp_label{.name = label.data()});
|
||||
}
|
||||
|
||||
inline std::unordered_map<std::string, Value> Node::Properties() const {
|
||||
inline std::map<std::string, Value> Node::Properties() const {
|
||||
mgp_properties_iterator *properties_iterator = mgp::MemHandlerCallback(vertex_iter_properties, ptr_);
|
||||
std::unordered_map<std::string, Value> property_map;
|
||||
std::map<std::string, Value> property_map;
|
||||
for (auto *property = mgp::properties_iterator_get(properties_iterator); property;
|
||||
property = mgp::properties_iterator_next(properties_iterator)) {
|
||||
property_map.emplace(std::string(property->name), Value(property->value));
|
||||
@@ -2773,17 +2661,6 @@ inline void Node::SetProperty(std::string property, Value value) {
|
||||
mgp::vertex_set_property(ptr_, property.data(), value.ptr());
|
||||
}
|
||||
|
||||
inline void Node::SetProperties(std::unordered_map<std::string_view, Value> properties) {
|
||||
mgp_map *map = mgp::MemHandlerCallback(map_make_empty);
|
||||
|
||||
for (auto const &[k, v] : properties) {
|
||||
mgp::map_insert(map, k.data(), v.ptr());
|
||||
}
|
||||
|
||||
mgp::vertex_set_properties(ptr_, map);
|
||||
mgp::map_destroy(map);
|
||||
}
|
||||
|
||||
inline void Node::RemoveProperty(std::string property) { SetProperty(property, Value()); }
|
||||
|
||||
inline Value Node::GetProperty(const std::string &property) const {
|
||||
@@ -2797,45 +2674,6 @@ inline bool Node::operator==(const Node &other) const { return util::NodesEqual(
|
||||
|
||||
inline bool Node::operator!=(const Node &other) const { return !(*this == other); }
|
||||
|
||||
// this functions is used both in relationship and node ToString
|
||||
inline std::string PropertiesToString(const std::map<std::string, Value> &property_map) {
|
||||
std::string properties;
|
||||
const auto map_size = property_map.size();
|
||||
size_t i = 0;
|
||||
for (const auto &[key, value] : property_map) {
|
||||
if (i == map_size - 1) {
|
||||
properties.append(std::string(key) + ": " + value.ToString());
|
||||
break;
|
||||
}
|
||||
properties.append(std::string(key) + ": " + value.ToString() + ", ");
|
||||
++i;
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
inline const std::string Node::ToString() const {
|
||||
std::string labels{", "};
|
||||
for (auto label : Labels()) {
|
||||
labels.append(":" + std::string(label));
|
||||
}
|
||||
if (labels == ", ") {
|
||||
labels = ""; // dont use labels if they dont exist
|
||||
}
|
||||
std::unordered_map<std::string, Value> properties_map{Properties()};
|
||||
std::map<std::string, Value> properties_map_sorted{};
|
||||
|
||||
for (const auto &[k, v] : properties_map) {
|
||||
properties_map_sorted.emplace(k, v);
|
||||
}
|
||||
std::string properties{PropertiesToString(properties_map_sorted)};
|
||||
|
||||
return "(id: " + std::to_string(Id().AsInt()) + labels + ", properties: {" + properties + "})";
|
||||
}
|
||||
|
||||
inline size_t Node::InDegree() const { return mgp::vertex_get_in_degree(ptr_); }
|
||||
|
||||
inline size_t Node::OutDegree() const { return mgp::vertex_get_out_degree(ptr_); }
|
||||
|
||||
// Relationship:
|
||||
|
||||
inline Relationship::Relationship(mgp_edge *ptr) : ptr_(mgp::MemHandlerCallback(edge_copy, ptr)) {}
|
||||
@@ -2876,9 +2714,9 @@ inline mgp::Id Relationship::Id() const { return Id::FromInt(mgp::edge_get_id(pt
|
||||
|
||||
inline std::string_view Relationship::Type() const { return mgp::edge_get_type(ptr_).name; }
|
||||
|
||||
inline std::unordered_map<std::string, Value> Relationship::Properties() const {
|
||||
inline std::map<std::string, Value> Relationship::Properties() const {
|
||||
mgp_properties_iterator *properties_iterator = mgp::MemHandlerCallback(edge_iter_properties, ptr_);
|
||||
std::unordered_map<std::string, Value> property_map;
|
||||
std::map<std::string, Value> property_map;
|
||||
for (mgp_property *property = mgp::properties_iterator_get(properties_iterator); property;
|
||||
property = mgp::properties_iterator_next(properties_iterator)) {
|
||||
property_map.emplace(property->name, Value(property->value));
|
||||
@@ -2891,17 +2729,6 @@ inline void Relationship::SetProperty(std::string property, Value value) {
|
||||
mgp::edge_set_property(ptr_, property.data(), value.ptr());
|
||||
}
|
||||
|
||||
inline void Relationship::SetProperties(std::unordered_map<std::string_view, Value> properties) {
|
||||
mgp_map *map = mgp::MemHandlerCallback(map_make_empty);
|
||||
|
||||
for (auto const &[k, v] : properties) {
|
||||
mgp::map_insert(map, k.data(), v.ptr());
|
||||
}
|
||||
|
||||
mgp::edge_set_properties(ptr_, map);
|
||||
mgp::map_destroy(map);
|
||||
}
|
||||
|
||||
inline void Relationship::RemoveProperty(std::string property) { SetProperty(property, Value()); }
|
||||
|
||||
inline Value Relationship::GetProperty(const std::string &property) const {
|
||||
@@ -2921,24 +2748,6 @@ inline bool Relationship::operator==(const Relationship &other) const {
|
||||
|
||||
inline bool Relationship::operator!=(const Relationship &other) const { return !(*this == other); }
|
||||
|
||||
inline const std::string Relationship::ToString() const {
|
||||
const auto from = From();
|
||||
const auto to = To();
|
||||
|
||||
const std::string type{Type()};
|
||||
std::unordered_map<std::string, Value> properties_map{Properties()};
|
||||
std::map<std::string, Value> properties_map_sorted{};
|
||||
|
||||
for (const auto &[k, v] : properties_map) {
|
||||
properties_map_sorted.emplace(k, v);
|
||||
}
|
||||
std::string properties{PropertiesToString(properties_map_sorted)};
|
||||
|
||||
const std::string relationship{"[type: " + type + ", id: " + std::to_string(Id().AsInt()) + ", properties: {" +
|
||||
properties + "}]"};
|
||||
|
||||
return from.ToString() + "-" + relationship + "->" + to.ToString();
|
||||
}
|
||||
// Path:
|
||||
|
||||
inline Path::Path(mgp_path *ptr) : ptr_(mgp::MemHandlerCallback(path_copy, ptr)) {}
|
||||
@@ -2997,38 +2806,10 @@ inline Relationship Path::GetRelationshipAt(size_t index) const {
|
||||
|
||||
inline void Path::Expand(const Relationship &relationship) { mgp::path_expand(ptr_, relationship.ptr_); }
|
||||
|
||||
inline void Path::Pop() { mgp::path_pop(ptr_); }
|
||||
|
||||
inline bool Path::operator==(const Path &other) const { return util::PathsEqual(ptr_, other.ptr_); }
|
||||
|
||||
inline bool Path::operator!=(const Path &other) const { return !(*this == other); }
|
||||
|
||||
inline const std::string Path::ToString() const {
|
||||
const auto length = Length();
|
||||
size_t i = 0;
|
||||
std::string return_string{""};
|
||||
for (i = 0; i < length; i++) {
|
||||
const auto node = GetNodeAt(i);
|
||||
return_string.append(node.ToString() + "-");
|
||||
|
||||
const Relationship rel = GetRelationshipAt(i);
|
||||
std::unordered_map<std::string, Value> properties_map{rel.Properties()};
|
||||
std::map<std::string, Value> properties_map_sorted{};
|
||||
|
||||
for (const auto &[k, v] : properties_map) {
|
||||
properties_map_sorted.emplace(k, v);
|
||||
}
|
||||
std::string properties{PropertiesToString(properties_map_sorted)};
|
||||
|
||||
return_string.append("[type: " + std::string(rel.Type()) + ", id: " + std::to_string(rel.Id().AsInt()) +
|
||||
", properties: {" + properties + "}]->");
|
||||
}
|
||||
|
||||
const auto node = GetNodeAt(i);
|
||||
return_string.append(node.ToString());
|
||||
return return_string;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Temporal types (Date, LocalTime, LocalDateTime, Duration) */
|
||||
@@ -3126,10 +2907,6 @@ inline bool Date::operator<(const Date &other) const {
|
||||
return is_less;
|
||||
}
|
||||
|
||||
inline const std::string Date::ToString() const {
|
||||
return std::to_string(Year()) + "-" + std::to_string(Month()) + "-" + std::to_string(Day());
|
||||
}
|
||||
|
||||
// LocalTime:
|
||||
|
||||
inline LocalTime::LocalTime(mgp_local_time *ptr) : ptr_(mgp::MemHandlerCallback(local_time_copy, ptr)) {}
|
||||
@@ -3229,11 +3006,6 @@ inline bool LocalTime::operator<(const LocalTime &other) const {
|
||||
return is_less;
|
||||
}
|
||||
|
||||
inline const std::string LocalTime::ToString() const {
|
||||
return std::to_string(Hour()) + ":" + std::to_string(Minute()) + ":" + std::to_string(Second()) + "," +
|
||||
std::to_string(Millisecond()) + std::to_string(Microsecond());
|
||||
}
|
||||
|
||||
// LocalDateTime:
|
||||
|
||||
inline LocalDateTime::LocalDateTime(mgp_local_date_time *ptr)
|
||||
@@ -3348,12 +3120,6 @@ inline bool LocalDateTime::operator<(const LocalDateTime &other) const {
|
||||
return is_less;
|
||||
}
|
||||
|
||||
inline const std::string LocalDateTime::ToString() const {
|
||||
return std::to_string(Year()) + "-" + std::to_string(Month()) + "-" + std::to_string(Day()) + "T" +
|
||||
std::to_string(Hour()) + ":" + std::to_string(Minute()) + ":" + std::to_string(Second()) + "," +
|
||||
std::to_string(Millisecond()) + std::to_string(Microsecond());
|
||||
}
|
||||
|
||||
// Duration:
|
||||
|
||||
inline Duration::Duration(mgp_duration *ptr) : ptr_(mgp::MemHandlerCallback(duration_copy, ptr)) {}
|
||||
@@ -3443,8 +3209,6 @@ inline bool Duration::operator<(const Duration &other) const {
|
||||
return is_less;
|
||||
}
|
||||
|
||||
inline const std::string Duration::ToString() const { return std::to_string(Microseconds()) + "ms"; }
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #endregion */
|
||||
@@ -3909,42 +3673,6 @@ inline std::ostream &operator<<(std::ostream &os, const mgp::Type &type) {
|
||||
}
|
||||
}
|
||||
|
||||
inline const std::string Value::ToString() const {
|
||||
const mgp::Type &type = Type();
|
||||
switch (type) {
|
||||
case Type::Null:
|
||||
return "";
|
||||
case Type::Bool:
|
||||
return ValueBool() ? "true" : "false";
|
||||
case Type::Int:
|
||||
return std::to_string(ValueInt());
|
||||
case Type::Double:
|
||||
return std::to_string(ValueDouble());
|
||||
case Type::String:
|
||||
return std::string(ValueString());
|
||||
case Type::Node:
|
||||
return ValueNode().ToString();
|
||||
case Type::Relationship:
|
||||
return ValueRelationship().ToString();
|
||||
case Type::Date:
|
||||
return ValueDate().ToString();
|
||||
case Type::LocalTime:
|
||||
return ValueLocalTime().ToString();
|
||||
case Type::LocalDateTime:
|
||||
return ValueLocalDateTime().ToString();
|
||||
case Type::Duration:
|
||||
return ValueDuration().ToString();
|
||||
case Type::List:
|
||||
return ValueList().ToString();
|
||||
case Type::Map:
|
||||
return ValueMap().ToString();
|
||||
case Type::Path:
|
||||
return ValuePath().ToString();
|
||||
default:
|
||||
throw ValueException("Undefined behaviour");
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Record */
|
||||
|
||||
@@ -479,12 +479,6 @@ class Properties:
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
def set_properties(self, properties: dict) -> None:
|
||||
if not self._vertex_or_edge.is_valid():
|
||||
raise InvalidContextError()
|
||||
|
||||
self._vertex_or_edge.set_properties(properties)
|
||||
|
||||
|
||||
class EdgeType:
|
||||
"""Type of an Edge."""
|
||||
@@ -983,24 +977,6 @@ class Path:
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
def pop(self):
|
||||
"""
|
||||
Remove the last node and the last relationship from the path.
|
||||
|
||||
Raises:
|
||||
InvalidContextError: If using an invalid `Path` instance
|
||||
OutOfRangeError: If the path contains no relationships.
|
||||
|
||||
Examples:
|
||||
```path.pop()```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
self._path.pop()
|
||||
# Invalidate our cached tuples
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
@property
|
||||
def vertices(self) -> typing.Tuple[Vertex, ...]:
|
||||
"""
|
||||
@@ -1041,10 +1017,6 @@ class Path:
|
||||
self._edges = tuple(Edge(self._path.edge_at(i)) for i in range(num_edges))
|
||||
return self._edges
|
||||
|
||||
@property
|
||||
def length(self) -> int:
|
||||
return self._path.size()
|
||||
|
||||
|
||||
class Record:
|
||||
"""Represents a record of resulting field values."""
|
||||
|
||||
@@ -929,25 +929,6 @@ class Path:
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
def pop(self):
|
||||
"""
|
||||
Remove the last node and the last relationship from the path.
|
||||
|
||||
Raises:
|
||||
InvalidContextError: If using an invalid `Path` instance
|
||||
OutOfRangeError: If the path contains no relationships.
|
||||
|
||||
Examples:
|
||||
```path.pop()```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
self._path.pop()
|
||||
|
||||
# Invalidate cached tuples
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
@property
|
||||
def vertices(self) -> typing.Tuple[Vertex, ...]:
|
||||
"""
|
||||
|
||||
@@ -15,6 +15,7 @@ set(GFLAGS_NOTHREADS OFF)
|
||||
# NOTE: config/generate.py depends on the gflags help XML format.
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(fmt 8.0.1)
|
||||
find_package(Jemalloc REQUIRED)
|
||||
find_package(ZLIB 1.2.11 REQUIRED)
|
||||
|
||||
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -98,17 +99,6 @@ macro(import_external_library name type library_location include_dir)
|
||||
import_library(${name} ${type} ${${_upper_name}_LIBRARY} ${${_upper_name}_INCLUDE_DIR})
|
||||
endmacro(import_external_library)
|
||||
|
||||
|
||||
macro(set_path_external_library name type library_location include_dir)
|
||||
string(TOUPPER ${name} _upper_name)
|
||||
set(${_upper_name}_LIBRARY ${library_location} CACHE FILEPATH
|
||||
"Path to ${name} library" FORCE)
|
||||
set(${_upper_name}_INCLUDE_DIR ${include_dir} CACHE FILEPATH
|
||||
"Path to ${name} include directory" FORCE)
|
||||
mark_as_advanced(${name}_LIBRARY ${name}_INCLUDE_DIR)
|
||||
endmacro(set_path_external_library)
|
||||
|
||||
|
||||
# setup antlr
|
||||
import_external_library(antlr4 STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/antlr4/runtime/Cpp/lib/libantlr4-runtime.a
|
||||
@@ -275,8 +265,3 @@ import_header_library(ctre ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# setup absl (cmake sub_directory tolerant)
|
||||
set(ABSL_PROPAGATE_CXX_STD ON)
|
||||
add_subdirectory(absl EXCLUDE_FROM_ALL)
|
||||
|
||||
# set Jemalloc
|
||||
set_path_external_library(jemalloc STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/jemalloc/lib/libjemalloc.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/jemalloc/include/)
|
||||
|
||||
@@ -124,7 +124,6 @@ declare -A primary_urls=(
|
||||
["librdtsc"]="http://$local_cache_host/git/librdtsc.git"
|
||||
["ctre"]="http://$local_cache_host/file/hanickadot/compile-time-regular-expressions/v3.7.2/single-header/ctre.hpp"
|
||||
["absl"]="https://$local_cache_host/git/abseil-cpp.git"
|
||||
["jemalloc"]="https://$local_cache_host/git/jemalloc.git"
|
||||
)
|
||||
|
||||
# The goal of secondary urls is to have links to the "source of truth" of
|
||||
@@ -152,7 +151,6 @@ declare -A secondary_urls=(
|
||||
["librdtsc"]="https://github.com/gabrieleara/librdtsc.git"
|
||||
["ctre"]="https://raw.githubusercontent.com/hanickadot/compile-time-regular-expressions/v3.7.2/single-header/ctre.hpp"
|
||||
["absl"]="https://github.com/abseil/abseil-cpp.git"
|
||||
["jemalloc"]="https://github.com/jemalloc/jemalloc.git"
|
||||
)
|
||||
|
||||
# antlr
|
||||
@@ -254,21 +252,3 @@ cd ..
|
||||
# abseil 20230125.3
|
||||
absl_ref="20230125.3"
|
||||
repo_clone_try_double "${primary_urls[absl]}" "${secondary_urls[absl]}" "absl" "$absl_ref"
|
||||
|
||||
|
||||
# jemalloc ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
JEMALLOC_COMMIT_VERSION="5.2.1"
|
||||
repo_clone_try_double "${secondary_urls[jemalloc]}" "${secondary_urls[jemalloc]}" "jemalloc" "$JEMALLOC_COMMIT_VERSION"
|
||||
|
||||
# this is hack for cmake in libs to set path, and for FindJemalloc to use Jemalloc_INCLUDE_DIR
|
||||
pushd jemalloc
|
||||
|
||||
./autogen.sh
|
||||
MALLOC_CONF="retain:false,percpu_arena:percpu,oversize_threshold:0,muzzy_decay_ms:5000,dirty_decay_ms:5000" \
|
||||
./configure \
|
||||
--disable-cxx \
|
||||
--enable-shared=no --prefix=$working_dir \
|
||||
--with-malloc-conf="retain:false,percpu_arena:percpu,oversize_threshold:0,muzzy_decay_ms:5000,dirty_decay_ms:5000"
|
||||
|
||||
make -j$CPUS install
|
||||
popd
|
||||
|
||||
@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
|
||||
3. using the Licensed Work to create a work or solution
|
||||
which competes (or might reasonably be expected to
|
||||
compete) with the Licensed Work.
|
||||
CHANGE DATE: 2027-13-09
|
||||
CHANGE DATE: 2027-02-08
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/etc/memgraph/memgraph.conf
|
||||
/etc/memgraph/apoc_compatibility_mappings.json
|
||||
/etc/memgraph/auth_module/ldap.example.yaml
|
||||
/etc/logrotate.d/memgraph
|
||||
|
||||
@@ -132,7 +132,6 @@ echo "Don't forget to switch to the 'memgraph' user to use Memgraph" || exit 1
|
||||
# Override CPACK_RPM_ABSOLUTE_INSTALL_FILES with our %config(noreplace), cpack
|
||||
# uses plain %config.
|
||||
%config(noreplace) "/etc/memgraph/memgraph.conf"
|
||||
%config(noreplace) "/etc/memgraph/apoc_compatibility_mappings.json"
|
||||
%config(noreplace) "/etc/memgraph/auth_module/ldap.example.yaml"
|
||||
%config(noreplace) "/etc/logrotate.d/memgraph"
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@ add_subdirectory(rpc)
|
||||
add_subdirectory(license)
|
||||
add_subdirectory(auth)
|
||||
add_subdirectory(audit)
|
||||
add_subdirectory(dbms)
|
||||
add_subdirectory(flags)
|
||||
add_subdirectory(distributed)
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
|
||||
@@ -34,13 +31,19 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# ----------------------------------------------------------------------------
|
||||
set(mg_single_node_v2_sources
|
||||
memgraph.cpp
|
||||
flags/isolation_level.cpp
|
||||
flags/memory_limit.cpp
|
||||
flags/log_level.cpp
|
||||
flags/general.cpp
|
||||
flags/audit.cpp
|
||||
flags/bolt.cpp
|
||||
)
|
||||
|
||||
# memgraph main executable
|
||||
add_executable(memgraph ${mg_single_node_v2_sources})
|
||||
target_include_directories(memgraph PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(memgraph stdc++fs Threads::Threads
|
||||
mg-telemetry mg-communication mg-memory mg-utils mg-license mg-settings mg-glue mg-flags)
|
||||
mg-telemetry mg-communication mg-memory mg-utils mg-license mg-settings mg-glue)
|
||||
|
||||
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
|
||||
# should be dynamically exported, so that `dlopen` can correctly link the
|
||||
@@ -73,7 +76,7 @@ if(lower_build_type STREQUAL "release")
|
||||
COMMENT "Stripping symbols and sections from memgraph")
|
||||
endif()
|
||||
|
||||
# Generate the configuration file under the build directory.
|
||||
# Generate the configuration file.
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_BINARY_DIR}/memgraph
|
||||
@@ -82,11 +85,6 @@ add_custom_command(TARGET memgraph POST_BUILD
|
||||
${CMAKE_SOURCE_DIR}/config/flags.yaml
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
COMMENT "Generating memgraph configuration file")
|
||||
# Copy the mappings file to the build directory.
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/config/mappings.json
|
||||
${CMAKE_BINARY_DIR}/config/apoc_compatibility_mappings.json)
|
||||
|
||||
# Everything here is under "memgraph" install component.
|
||||
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "memgraph")
|
||||
@@ -122,9 +120,6 @@ install(FILES ${CMAKE_SOURCE_DIR}/include/mgp.hpp
|
||||
# Install the config file (must use absolute path).
|
||||
install(FILES ${CMAKE_BINARY_DIR}/config/memgraph.conf
|
||||
DESTINATION /etc/memgraph RENAME memgraph.conf)
|
||||
# Install the mappings file (must use absolute path).
|
||||
install(FILES ${CMAKE_BINARY_DIR}/config/apoc_compatibility_mappings.json
|
||||
DESTINATION /etc/memgraph RENAME apoc_compatibility_mappings.json)
|
||||
|
||||
# Install logrotate configuration (must use absolute path).
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/release/logrotate.conf
|
||||
|
||||
@@ -54,7 +54,7 @@ class SessionException : public utils::BasicException {
|
||||
* @tparam TOutputStream type of output stream that will be used
|
||||
*/
|
||||
template <typename TInputStream, typename TOutputStream>
|
||||
class Session {
|
||||
class Session : public dbms::SessionInterface {
|
||||
public:
|
||||
using TEncoder = Encoder<ChunkedEncoderBuffer<TOutputStream>>;
|
||||
|
||||
@@ -159,7 +159,6 @@ class Session {
|
||||
break;
|
||||
case State::Idle:
|
||||
case State::Result:
|
||||
at_least_one_run_ = true;
|
||||
state_ = StateExecutingRun(*this, state_);
|
||||
break;
|
||||
case State::Error:
|
||||
@@ -181,12 +180,6 @@ class Session {
|
||||
}
|
||||
}
|
||||
|
||||
void HandleError() {
|
||||
if (!at_least_one_run_) {
|
||||
spdlog::info("Sudden connection loss. Make sure the client supports Memgraph.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rethink if there is a way to hide some members. At the momement all of them are public.
|
||||
TInputStream &input_stream_;
|
||||
TOutputStream &output_stream_;
|
||||
@@ -199,7 +192,6 @@ class Session {
|
||||
|
||||
bool handshake_done_{false};
|
||||
State state_{State::Handshake};
|
||||
bool at_least_one_run_{false};
|
||||
|
||||
struct Version {
|
||||
uint8_t major;
|
||||
@@ -208,8 +200,8 @@ class Session {
|
||||
|
||||
Version version_;
|
||||
|
||||
virtual std::string GetCurrentDB() const = 0;
|
||||
std::string UUID() const { return session_uuid_; }
|
||||
std::string GetDatabaseName() const override = 0;
|
||||
std::string UUID() const final { return session_uuid_; }
|
||||
|
||||
private:
|
||||
void ClientFailureInvalidData() {
|
||||
|
||||
@@ -208,7 +208,7 @@ State HandleRunV1(TSession &session, const State state, const Marker marker) {
|
||||
|
||||
DMG_ASSERT(!session.encoder_buffer_.HasData(), "There should be no data to write in this state");
|
||||
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetCurrentDB(), query.ValueString());
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
@@ -272,7 +272,7 @@ State HandleRunV4(TSession &session, const State state, const Marker marker) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetCurrentDB(), query.ValueString());
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
|
||||
@@ -174,7 +174,7 @@ State SendSuccessMessage(TSession &session) {
|
||||
// we send a hardcoded value for now.
|
||||
std::map<std::string, Value> metadata{{"connection_id", "bolt-1"}};
|
||||
if (auto server_name = session.GetServerNameForInit(); server_name) {
|
||||
metadata.insert({"server", std::move(*server_name)});
|
||||
metadata.insert({"server", *server_name});
|
||||
}
|
||||
bool success_sent = session.encoder_.MessageSuccess(metadata);
|
||||
if (!success_sent) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -15,10 +15,6 @@
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
// Centos 7 OpenSSL includes libkrb5 which has brings in macros TRUE and FALSE. undef to prevent issues.
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
|
||||
#include "communication/buffer.hpp"
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/init.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -17,10 +17,6 @@
|
||||
#include <openssl/ssl.h>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
// Centos 7 OpenSSL includes libkrb5 which has brings in macros TRUE and FALSE. undef to prevent issues.
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
|
||||
namespace memgraph::communication {
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
// Centos 7 OpenSSL includes libkrb5 which has brings in macros TRUE and FALSE. undef to prevent issues.
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
|
||||
#include "communication/buffer.hpp"
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/exceptions.hpp"
|
||||
|
||||
@@ -110,7 +110,11 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
return std::shared_ptr<WebsocketSession>(new WebsocketSession(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
~WebsocketSession() { session_context_->Delete(session_); }
|
||||
#else
|
||||
~WebsocketSession() = default;
|
||||
#endif
|
||||
|
||||
WebsocketSession(const WebsocketSession &) = delete;
|
||||
WebsocketSession &operator=(const WebsocketSession &) = delete;
|
||||
@@ -167,15 +171,14 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
: ws_(std::move(socket)),
|
||||
strand_{boost::asio::make_strand(ws_.get_executor())},
|
||||
output_stream_([this](const uint8_t *data, size_t len, bool /*have_more*/) { return Write(data, len); }),
|
||||
session_{session_context->ic, endpoint, input_buffer_.read_end(), &output_stream_, session_context->auth,
|
||||
#ifdef MG_ENTERPRISE
|
||||
session_context->audit_log
|
||||
#endif
|
||||
},
|
||||
session_{*session_context, endpoint, input_buffer_.read_end(), &output_stream_},
|
||||
session_context_{session_context},
|
||||
endpoint_{endpoint},
|
||||
remote_endpoint_{ws_.next_layer().socket().remote_endpoint()},
|
||||
service_name_{service_name} {
|
||||
#ifdef MG_ENTERPRISE
|
||||
session_context_->Register(session_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnAccept(boost::beast::error_code ec) {
|
||||
@@ -283,7 +286,11 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
return std::shared_ptr<Session>(new Session(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
~Session() { session_context_->Delete(session_); }
|
||||
#else
|
||||
~Session() = default;
|
||||
#endif
|
||||
|
||||
Session(const Session &) = delete;
|
||||
Session(Session &&) = delete;
|
||||
@@ -359,17 +366,17 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
: socket_(CreateSocket(std::move(socket), server_context)),
|
||||
strand_{boost::asio::make_strand(GetExecutor())},
|
||||
output_stream_([this](const uint8_t *data, size_t len, bool have_more) { return Write(data, len, have_more); }),
|
||||
session_{session_context->ic, endpoint, input_buffer_.read_end(), &output_stream_, session_context->auth,
|
||||
#ifdef MG_ENTERPRISE
|
||||
session_context->audit_log
|
||||
#endif
|
||||
},
|
||||
session_{*session_context, endpoint, input_buffer_.read_end(), &output_stream_},
|
||||
session_context_{session_context},
|
||||
endpoint_{endpoint},
|
||||
remote_endpoint_{GetRemoteEndpoint()},
|
||||
service_name_{service_name},
|
||||
timeout_seconds_(inactivity_timeout_sec),
|
||||
timeout_timer_(GetExecutor()) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
// TODO Try to remove Register (see comment at SessionInterface declaration)
|
||||
session_context_->Register(session_);
|
||||
#endif
|
||||
ExecuteForSocket([](auto &&socket) {
|
||||
socket.lowest_layer().set_option(tcp::no_delay(true)); // enable PSH
|
||||
socket.lowest_layer().set_option(boost::asio::socket_base::keep_alive(true)); // enable SO_KEEPALIVE
|
||||
@@ -406,8 +413,6 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
|
||||
void OnRead(const boost::system::error_code &ec, const size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
// TODO Check if client disconnected
|
||||
session_.HandleError();
|
||||
return OnError(ec);
|
||||
}
|
||||
input_buffer_.write_end()->Written(bytes_transferred);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
add_library(mg-dbms STATIC database.cpp)
|
||||
target_link_libraries(mg-dbms mg-utils mg-storage-v2 mg-query)
|
||||
@@ -1,37 +0,0 @@
|
||||
// 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 "dbms/database.hpp"
|
||||
#include "flags/storage_mode.hpp"
|
||||
#include "storage/v2/disk/storage.hpp"
|
||||
#include "storage/v2/inmemory/storage.hpp"
|
||||
#include "storage/v2/storage_mode.hpp"
|
||||
|
||||
template struct memgraph::utils::Gatekeeper<memgraph::dbms::Database>;
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
Database::Database(const storage::Config &config)
|
||||
: trigger_store_(config.durability.storage_directory / "triggers"),
|
||||
streams_{config.durability.storage_directory / "streams"} {
|
||||
if (config.storage_mode == memgraph::storage::StorageMode::ON_DISK_TRANSACTIONAL || config.force_on_disk ||
|
||||
utils::DirExists(config.disk.main_storage_directory)) {
|
||||
storage_ = std::make_unique<storage::DiskStorage>(config);
|
||||
} else {
|
||||
storage_ = std::make_unique<storage::InMemoryStorage>(config, config.storage_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void Database::SwitchToOnDisk() {
|
||||
storage_ = std::make_unique<memgraph::storage::DiskStorage>(std::move(storage_->config_));
|
||||
}
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
@@ -1,153 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "query/cypher_query_interpreter.hpp"
|
||||
#include "query/stream/streams.hpp"
|
||||
#include "query/trigger.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/gatekeeper.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/**
|
||||
* @brief Class containing everything associated with a single Database
|
||||
*
|
||||
*/
|
||||
class Database {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Database object
|
||||
*
|
||||
* @param config storage configuration
|
||||
*/
|
||||
explicit Database(const storage::Config &config);
|
||||
|
||||
/**
|
||||
* @brief Returns the raw storage pointer.
|
||||
* @note Ideally everybody would be using an accessor
|
||||
* TODO: Remove
|
||||
*
|
||||
* @return storage::Storage*
|
||||
*/
|
||||
storage::Storage *storage() { return storage_.get(); }
|
||||
|
||||
/**
|
||||
* @brief Storage's Accessor
|
||||
*
|
||||
* @param override_isolation_level
|
||||
* @return std::unique_ptr<storage::Storage::Accessor>
|
||||
*/
|
||||
std::unique_ptr<storage::Storage::Accessor> Access(
|
||||
std::optional<storage::IsolationLevel> override_isolation_level = {}) {
|
||||
return storage_->Access(override_isolation_level);
|
||||
}
|
||||
|
||||
std::unique_ptr<storage::Storage::Accessor> UniqueAccess(
|
||||
std::optional<storage::IsolationLevel> override_isolation_level = {}) {
|
||||
return storage_->UniqueAccess(override_isolation_level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unique storage identified (name)
|
||||
*
|
||||
* @return const std::string&
|
||||
*/
|
||||
const std::string &id() const { return storage_->id(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the storage configuration
|
||||
*
|
||||
* @return const storage::Config&
|
||||
*/
|
||||
const storage::Config &config() const { return storage_->config_; }
|
||||
|
||||
/**
|
||||
* @brief Get the storage mode
|
||||
*
|
||||
* @return storage::StorageMode
|
||||
*/
|
||||
storage::StorageMode GetStorageMode() const { return storage_->GetStorageMode(); }
|
||||
|
||||
/**
|
||||
* @brief Get the storage info
|
||||
*
|
||||
* @return storage::StorageInfo
|
||||
*/
|
||||
storage::StorageInfo GetInfo() const { return storage_->GetInfo(); }
|
||||
|
||||
/**
|
||||
* @brief Switch storage to OnDisk
|
||||
*
|
||||
*/
|
||||
void SwitchToOnDisk();
|
||||
|
||||
/**
|
||||
* @brief Returns the raw TriggerStore pointer
|
||||
*
|
||||
* @return query::TriggerStore*
|
||||
*/
|
||||
query::TriggerStore *trigger_store() { return &trigger_store_; }
|
||||
|
||||
/**
|
||||
* @brief Returns the raw Streams pointer
|
||||
*
|
||||
* @return query::stream::Streams*
|
||||
*/
|
||||
query::stream::Streams *streams() { return &streams_; }
|
||||
|
||||
/**
|
||||
* @brief Returns the raw ThreadPool pointer (used for after commit triggers)
|
||||
*
|
||||
* @return utils::ThreadPool*
|
||||
*/
|
||||
utils::ThreadPool *thread_pool() { return &after_commit_trigger_pool_; }
|
||||
|
||||
/**
|
||||
* @brief Add task to the after commit trigger thread pool
|
||||
*
|
||||
* @param new_task
|
||||
*/
|
||||
void AddTask(std::function<void()> new_task) { after_commit_trigger_pool_.AddTask(std::move(new_task)); }
|
||||
|
||||
/**
|
||||
* @brief Returns the PlanCache vector raw pointer
|
||||
*
|
||||
* @return utils::SkipList<query::PlanCacheEntry>*
|
||||
*/
|
||||
utils::SkipList<query::PlanCacheEntry> *plan_cache() { return &plan_cache_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<storage::Storage> storage_; //!< Underlying storage
|
||||
query::TriggerStore trigger_store_; //!< Triggers associated with the storage
|
||||
utils::ThreadPool after_commit_trigger_pool_{1}; //!< Thread pool for executing after commit triggers
|
||||
query::stream::Streams streams_; //!< Streams associated with the storage
|
||||
|
||||
// TODO: Move to a better place
|
||||
utils::SkipList<query::PlanCacheEntry> plan_cache_; //!< Plan cache associated with the storage
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
extern template struct memgraph::utils::Gatekeeper<memgraph::dbms::Database>;
|
||||
|
||||
namespace memgraph::dbms {
|
||||
using DatabaseAccess = memgraph::utils::Gatekeeper<memgraph::dbms::Database>::Accessor;
|
||||
} // namespace memgraph::dbms
|
||||
@@ -1,97 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dbms/database.hpp"
|
||||
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/* NOTE
|
||||
* The Database object is shared. All the higher-level function calls should be protected.
|
||||
* Storage function calls should already be protected; add protection where needed.
|
||||
*
|
||||
* Current implementation uses a handler of Database objects. It owns them and gives
|
||||
* Gatekeeper::Accessor to it. These guarantee that the object won't be
|
||||
* destroyed unless no one is using it.
|
||||
*/
|
||||
|
||||
/**Config
|
||||
* @brief Multi-database storage handler
|
||||
*
|
||||
*/
|
||||
class DatabaseHandler : public Handler<Database> {
|
||||
public:
|
||||
using HandlerT = Handler<Database>;
|
||||
|
||||
/**
|
||||
* @brief Generate new storage associated with the passed name.
|
||||
*
|
||||
* @param name Name associating the new interpreter context
|
||||
* @param config Storage configuration
|
||||
* @return HandlerT::NewResult
|
||||
*/
|
||||
HandlerT::NewResult New(std::string_view name, storage::Config config) {
|
||||
// Control that no one is using the same data directory
|
||||
if (std::any_of(begin(), end(), [&](auto &elem) {
|
||||
auto db_acc = elem.second.access();
|
||||
MG_ASSERT(db_acc.has_value(), "Gatekeeper in invalid state");
|
||||
return db_acc->get()->config().durability.storage_directory == config.durability.storage_directory;
|
||||
})) {
|
||||
spdlog::info("Tried to generate new storage using a claimed directory.");
|
||||
return NewError::EXISTS;
|
||||
}
|
||||
config.name = name; // Set storage id via config
|
||||
return HandlerT::New(std::piecewise_construct, name, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief All currently active storage.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
*/
|
||||
std::vector<std::string> All() const {
|
||||
std::vector<std::string> res;
|
||||
res.reserve(std::distance(cbegin(), cend()));
|
||||
std::for_each(cbegin(), cend(), [&](const auto &elem) { res.push_back(elem.first); });
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the associated storage's configuration
|
||||
*
|
||||
* @param name
|
||||
* @return std::optional<storage::Config>
|
||||
*/
|
||||
std::optional<storage::Config> GetConfig(std::string_view name) {
|
||||
auto db = Get(name);
|
||||
if (db) {
|
||||
return (*db)->config();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
#endif
|
||||
@@ -1,390 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "dbms/database_handler.hpp"
|
||||
#include "global.hpp"
|
||||
#include "query/config.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "storage/v2/durability/durability.hpp"
|
||||
#include "storage/v2/durability/paths.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
using DeleteResult = utils::BasicResult<DeleteError>;
|
||||
|
||||
/**
|
||||
* @brief Multi-database session contexts handler.
|
||||
*/
|
||||
class DbmsHandler {
|
||||
public:
|
||||
using LockT = utils::RWLock;
|
||||
using NewResultT = utils::BasicResult<NewError, DatabaseAccess>;
|
||||
|
||||
struct Statistics {
|
||||
uint64_t num_vertex; //!< Sum of vertexes in every database
|
||||
uint64_t num_edges; //!< Sum of edges in every database
|
||||
uint64_t num_databases; //! number of isolated databases
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize the handler.
|
||||
*
|
||||
* @param configs storage and interpreter configurations
|
||||
* @param auth pointer to the global authenticator
|
||||
* @param recovery_on_startup restore databases (and its content) and authentication data
|
||||
* @param delete_on_drop when dropping delete any associated directories on disk
|
||||
*/
|
||||
DbmsHandler(storage::Config config, auto *auth, bool recovery_on_startup, bool delete_on_drop)
|
||||
: lock_{utils::RWLock::Priority::READ}, default_config_{std::move(config)}, delete_on_drop_(delete_on_drop) {
|
||||
// TODO: Decouple storage config from dbms config
|
||||
// TODO: Save individual db configs inside the kvstore and restore from there
|
||||
storage::UpdatePaths(*default_config_, default_config_->durability.storage_directory / "databases");
|
||||
const auto &db_dir = default_config_->durability.storage_directory;
|
||||
const auto durability_dir = db_dir / ".durability";
|
||||
utils::EnsureDirOrDie(db_dir);
|
||||
utils::EnsureDirOrDie(durability_dir);
|
||||
durability_ = std::make_unique<kvstore::KVStore>(durability_dir);
|
||||
|
||||
// Generate the default database
|
||||
MG_ASSERT(!NewDefault_().HasError(), "Failed while creating the default DB.");
|
||||
|
||||
// Recover previous databases
|
||||
if (recovery_on_startup) {
|
||||
for (const auto &[name, _] : *durability_) {
|
||||
if (name == kDefaultDB) continue; // Already set
|
||||
spdlog::info("Restoring database {}.", name);
|
||||
MG_ASSERT(!New_(name).HasError(), "Failed while creating database {}.", name);
|
||||
spdlog::info("Database {} restored.", name);
|
||||
}
|
||||
} else { // Clear databases from the durability list and auth
|
||||
auto locked_auth = auth->Lock();
|
||||
for (const auto &[name, _] : *durability_) {
|
||||
if (name == kDefaultDB) continue;
|
||||
locked_auth->DeleteDatabase(name);
|
||||
durability_->Delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Database associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New(const std::string &name) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
return New_(name, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the context associated with the "name" database
|
||||
*
|
||||
* @param name
|
||||
* @return DatabaseAccess
|
||||
* @throw UnknownDatabaseException if database not found
|
||||
*/
|
||||
DatabaseAccess Get(std::string_view name) {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return Get_(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete database.
|
||||
*
|
||||
* @param db_name database name
|
||||
* @return DeleteResult error on failure
|
||||
*/
|
||||
DeleteResult Delete(const std::string &db_name) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
if (db_name == kDefaultDB) {
|
||||
// MSG cannot delete the default db
|
||||
return DeleteError::DEFAULT_DB;
|
||||
}
|
||||
|
||||
const auto storage_path = StorageDir_(db_name);
|
||||
if (!storage_path) return DeleteError::NON_EXISTENT;
|
||||
|
||||
// Check if db exists
|
||||
try {
|
||||
// Low level handlers
|
||||
if (!db_handler_.Delete(db_name)) {
|
||||
return DeleteError::USING;
|
||||
}
|
||||
} catch (utils::BasicException &) {
|
||||
return DeleteError::NON_EXISTENT;
|
||||
}
|
||||
|
||||
// Remove from durability list
|
||||
if (durability_) durability_->Delete(db_name);
|
||||
|
||||
// Delete disk storage
|
||||
if (delete_on_drop_) {
|
||||
std::error_code ec;
|
||||
(void)std::filesystem::remove_all(*storage_path, ec);
|
||||
if (ec) {
|
||||
spdlog::error("Failed to clean disk while deleting database \"{}\".", db_name);
|
||||
defunct_dbs_.emplace(db_name);
|
||||
return DeleteError::DISK_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from defunct_dbs_ (in case a second delete call was successful)
|
||||
defunct_dbs_.erase(db_name);
|
||||
|
||||
return {}; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return all active databases.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
*/
|
||||
std::vector<std::string> All() const {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return db_handler_.All();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the number of vertex across all databases.
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
Statistics Info() {
|
||||
// TODO: Handle overflow?
|
||||
uint64_t nv = 0;
|
||||
uint64_t ne = 0;
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
const uint64_t ndb = std::distance(db_handler_.cbegin(), db_handler_.cend());
|
||||
for (auto &[_, db_gk] : db_handler_) {
|
||||
auto db_acc_opt = db_gk.access();
|
||||
if (!db_acc_opt) continue;
|
||||
auto &db_acc = *db_acc_opt;
|
||||
const auto &info = db_acc->GetInfo();
|
||||
nv += info.vertex_count;
|
||||
ne += info.edge_count;
|
||||
}
|
||||
return {nv, ne, ndb};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore triggers for all currently defined databases.
|
||||
* @note: Triggers can execute query procedures, so we need to reload the modules first and then the triggers
|
||||
*
|
||||
* @param ic global InterpreterContext
|
||||
*/
|
||||
void RestoreTriggers(query::InterpreterContext *ic) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
for (auto &[_, db_gk] : db_handler_) {
|
||||
auto db_acc_opt = db_gk.access();
|
||||
if (!db_acc_opt) continue;
|
||||
auto &db_acc = *db_acc_opt;
|
||||
spdlog::debug("Restoring trigger for database \"{}\"", db_acc->id());
|
||||
auto storage_accessor = db_acc->Access();
|
||||
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
|
||||
db_acc->trigger_store()->RestoreTriggers(&ic->ast_cache, &dba, ic->config.query, ic->auth_checker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore streams of all currently defined databases.
|
||||
* @note: Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
||||
*
|
||||
* @param ic global InterpreterContext
|
||||
*/
|
||||
void RestoreStreams(query::InterpreterContext *ic) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
for (auto &[_, db_gk] : db_handler_) {
|
||||
auto db_acc = db_gk.access();
|
||||
if (!db_acc) continue;
|
||||
auto *db = db_acc->get();
|
||||
spdlog::debug("Restoring streams for database \"{}\"", db->id());
|
||||
db->streams()->RestoreStreams(*db_acc, ic);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief return the storage directory of the associated database
|
||||
*
|
||||
* @param name Database name
|
||||
* @return std::optional<std::filesystem::path>
|
||||
*/
|
||||
std::optional<std::filesystem::path> StorageDir_(const std::string &name) {
|
||||
const auto conf = db_handler_.GetConfig(name);
|
||||
if (conf) {
|
||||
return conf->durability.storage_directory;
|
||||
}
|
||||
spdlog::debug("Failed to find storage dir for database \"{}\"", name);
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Database associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name) { return New_(name, name); }
|
||||
|
||||
/**
|
||||
* @brief Create a new Database associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @param storage_subdir undelying RocksDB directory
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name, std::filesystem::path storage_subdir) {
|
||||
if (default_config_) {
|
||||
auto config_copy = *default_config_;
|
||||
storage::UpdatePaths(config_copy, default_config_->durability.storage_directory / storage_subdir);
|
||||
return New_(name, config_copy);
|
||||
}
|
||||
spdlog::info("Trying to generate session context without any configurations.");
|
||||
return NewError::NO_CONFIGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Database associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @param storage_config storage configuration
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name, storage::Config &storage_config) {
|
||||
if (defunct_dbs_.contains(name)) {
|
||||
spdlog::warn("Failed to generate database due to the unknown state of the previously defunct database \"{}\".",
|
||||
name);
|
||||
return NewError::DEFUNCT;
|
||||
}
|
||||
|
||||
auto new_db = db_handler_.New(name, storage_config);
|
||||
if (new_db.HasValue()) {
|
||||
// Success
|
||||
if (durability_) durability_->Put(name, "ok"); // TODO: Serialize the configuration?
|
||||
return new_db.GetValue();
|
||||
}
|
||||
return new_db.GetError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new Database associated with the default database
|
||||
*
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT NewDefault_() {
|
||||
// Create the default DB in the root (this is how it was done pre multi-tenancy)
|
||||
auto res = New_(kDefaultDB, "..");
|
||||
if (res.HasValue()) {
|
||||
// For back-compatibility...
|
||||
// Recreate the dbms layout for the default db and symlink to the root
|
||||
const auto dir = StorageDir_(kDefaultDB);
|
||||
MG_ASSERT(dir, "Failed to find storage path.");
|
||||
const auto main_dir = *dir / "databases" / kDefaultDB;
|
||||
|
||||
if (!std::filesystem::exists(main_dir)) {
|
||||
std::filesystem::create_directory(main_dir);
|
||||
}
|
||||
|
||||
// Force link on-disk directories
|
||||
const auto conf = db_handler_.GetConfig(kDefaultDB);
|
||||
MG_ASSERT(conf, "No configuration for the default database.");
|
||||
const auto &tmp_conf = conf->disk;
|
||||
std::vector<std::filesystem::path> to_link{
|
||||
tmp_conf.main_storage_directory, tmp_conf.label_index_directory,
|
||||
tmp_conf.label_property_index_directory, tmp_conf.unique_constraints_directory,
|
||||
tmp_conf.name_id_mapper_directory, tmp_conf.id_name_mapper_directory,
|
||||
tmp_conf.durability_directory, tmp_conf.wal_directory,
|
||||
};
|
||||
|
||||
// Add in-memory paths
|
||||
// Some directories are redundant (skip those)
|
||||
const std::vector<std::string> skip{".lock", "audit_log", "auth", "databases", "internal_modules", "settings"};
|
||||
for (auto const &item : std::filesystem::directory_iterator{*dir}) {
|
||||
const auto dir_name = std::filesystem::relative(item.path(), item.path().parent_path());
|
||||
if (std::find(skip.begin(), skip.end(), dir_name) != skip.end()) continue;
|
||||
to_link.push_back(item.path());
|
||||
}
|
||||
|
||||
// Symlink to root dir
|
||||
for (auto const &item : to_link) {
|
||||
const auto dir_name = std::filesystem::relative(item, item.parent_path());
|
||||
const auto link = main_dir / dir_name;
|
||||
const auto to = std::filesystem::relative(item, main_dir);
|
||||
if (!std::filesystem::is_symlink(link) && !std::filesystem::exists(link)) {
|
||||
std::filesystem::create_directory_symlink(to, link);
|
||||
} else { // Check existing link
|
||||
std::error_code ec;
|
||||
const auto test_link = std::filesystem::read_symlink(link, ec);
|
||||
if (ec || test_link != to) {
|
||||
MG_ASSERT(false,
|
||||
"Memgraph storage directory incompatible with new version.\n"
|
||||
"Please use a clean directory or remove \"{}\" and try again.",
|
||||
link.string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the DatabaseAccess for the database associated with the "name"
|
||||
*
|
||||
* @param name
|
||||
* @return DatabaseAccess
|
||||
* @throw UnknownDatabaseException if trying to get unknown database
|
||||
*/
|
||||
DatabaseAccess Get_(std::string_view name) {
|
||||
auto db = db_handler_.Get(name);
|
||||
if (db) {
|
||||
return *db;
|
||||
}
|
||||
throw UnknownDatabaseException("Tried to retrieve an unknown database \"{}\".", name);
|
||||
}
|
||||
|
||||
// Should storage objects ever be deleted?
|
||||
mutable LockT lock_; //!< protective lock
|
||||
DatabaseHandler db_handler_; //!< multi-tenancy storage handler
|
||||
std::optional<storage::Config> default_config_; //!< Storage configuration used when creating new databases
|
||||
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
|
||||
std::set<std::string> defunct_dbs_; //!< Databases that are in an unknown state due to various failures
|
||||
bool delete_on_drop_; //!< Flag defining if dropping storage also deletes its directory
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
@@ -60,4 +60,51 @@ class UnknownDatabaseException : public utils::BasicException {
|
||||
using utils::BasicException::BasicException;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Session interface used by the DBMS to handle the the active sessions.
|
||||
* @todo Try to remove this dependency from SessionContextHandler. OnDelete could be removed, as it only does an assert.
|
||||
* OnChange could be removed if SetFor returned the pointer and the called then handled the OnChange execution.
|
||||
* However, the interface is very useful to decouple the interpreter's query execution and the sessions themselves.
|
||||
*/
|
||||
class SessionInterface {
|
||||
public:
|
||||
SessionInterface() = default;
|
||||
virtual ~SessionInterface() = default;
|
||||
|
||||
SessionInterface(const SessionInterface &) = default;
|
||||
SessionInterface &operator=(const SessionInterface &) = default;
|
||||
SessionInterface(SessionInterface &&) noexcept = default;
|
||||
SessionInterface &operator=(SessionInterface &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Return the unique string identifying the session.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
virtual std::string UUID() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the currently active database.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
virtual std::string GetDatabaseName() const = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
* @brief Gets called on database change.
|
||||
*
|
||||
* @return SetForResult enum (SUCCESS, ALREADY_SET or FAIL)
|
||||
*/
|
||||
virtual dbms::SetForResult OnChange(const std::string &) = 0;
|
||||
|
||||
/**
|
||||
* @brief Callback that gets called on database delete (drop).
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
virtual bool OnDelete(const std::string &) = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
@@ -18,21 +18,21 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "global.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/gatekeeper.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/sync_ptr.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/**
|
||||
* @brief Generic multi-database content handler.
|
||||
*
|
||||
* @tparam T
|
||||
* @tparam TContext
|
||||
* @tparam TConfig
|
||||
*/
|
||||
template <typename T>
|
||||
template <typename TContext, typename TConfig>
|
||||
class Handler {
|
||||
public:
|
||||
using NewResult = utils::BasicResult<NewError, typename utils::Gatekeeper<T>::Accessor>;
|
||||
using NewResult = utils::BasicResult<NewError, std::shared_ptr<TContext>>;
|
||||
|
||||
/**
|
||||
* @brief Empty Handler constructor.
|
||||
@@ -43,65 +43,67 @@ class Handler {
|
||||
/**
|
||||
* @brief Generate a new context and corresponding configuration.
|
||||
*
|
||||
* @tparam Args Variadic template of constructor arguments of T
|
||||
* @param name Name associated with the new T
|
||||
* @param args Arguments passed to the constructor of T
|
||||
* @tparam T1 Variadic template of context constructor arguments
|
||||
* @tparam T2 Variadic template of config constructor arguments
|
||||
* @param name Name associated with the new context/config pair
|
||||
* @param args1 Arguments passed (as a tuple) to the context constructor
|
||||
* @param args2 Arguments passed (as a tuple) to the config constructor
|
||||
* @return NewResult
|
||||
*/
|
||||
template <typename... Args>
|
||||
NewResult New(std::piecewise_construct_t /* marker */, std::string_view name, Args... args) {
|
||||
// Make sure the emplace will succeed, since we don't want to create temporary objects that could break something
|
||||
if (!Has(name)) {
|
||||
auto [itr, _] = items_.emplace(std::piecewise_construct, std::forward_as_tuple(name),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
auto db_acc = itr->second.access();
|
||||
if (db_acc) return std::move(*db_acc);
|
||||
return NewError::DEFUNCT;
|
||||
}
|
||||
spdlog::info("Item with name \"{}\" already exists.", name);
|
||||
return NewError::EXISTS;
|
||||
template <typename... T1, typename... T2>
|
||||
NewResult New(std::string name, std::tuple<T1...> args1, std::tuple<T2...> args2) {
|
||||
return New_(name, args1, args2, std::make_index_sequence<sizeof...(T1)>{},
|
||||
std::make_index_sequence<sizeof...(T2)>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get pointer to context.
|
||||
*
|
||||
* @param name Name associated with the wanted context
|
||||
* @return std::optional<typename utils::Gatekeeper<T>::Accessor>
|
||||
* @return std::optional<std::shared_ptr<TContext>>
|
||||
*/
|
||||
std::optional<typename utils::Gatekeeper<T>::Accessor> Get(std::string_view name) {
|
||||
std::optional<std::shared_ptr<TContext>> Get(const std::string &name) {
|
||||
if (auto search = items_.find(name); search != items_.end()) {
|
||||
return search->second.access();
|
||||
return search->second.get();
|
||||
}
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete the context associated with the name.
|
||||
* @brief Get the config.
|
||||
*
|
||||
* @param name Name associated with the context to delete
|
||||
* @param name Name associated with the wanted config
|
||||
* @return std::optional<TConfig>
|
||||
*/
|
||||
std::optional<TConfig> GetConfig(const std::string &name) const {
|
||||
if (auto search = items_.find(name); search != items_.end()) {
|
||||
return search->second.config();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete the context/config pair associated with the name.
|
||||
*
|
||||
* @param name Name associated with the context/config pair to delete
|
||||
* @return true on success
|
||||
* @throw BasicException
|
||||
*/
|
||||
bool Delete(const std::string &name) {
|
||||
if (auto itr = items_.find(name); itr != items_.end()) {
|
||||
auto db_acc = itr->second.access();
|
||||
if (db_acc && db_acc->try_delete()) {
|
||||
db_acc->reset();
|
||||
items_.erase(itr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
itr->second.DestroyAndSync();
|
||||
items_.erase(itr);
|
||||
return true;
|
||||
}
|
||||
throw utils::BasicException("Unknown item \"{}\".", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a name is already used.
|
||||
*
|
||||
* @param name Name to check
|
||||
* @return true if a T is already associated with the name
|
||||
* @return true if a context/config pair is already associated with the name
|
||||
*/
|
||||
bool Has(std::string_view name) const { return items_.find(name) != items_.end(); }
|
||||
bool Has(const std::string &name) const { return items_.find(name) != items_.end(); }
|
||||
|
||||
auto begin() { return items_.begin(); }
|
||||
auto end() { return items_.end(); }
|
||||
@@ -110,16 +112,31 @@ class Handler {
|
||||
auto cbegin() const { return items_.cbegin(); }
|
||||
auto cend() const { return items_.cend(); }
|
||||
|
||||
struct string_hash {
|
||||
using is_transparent = void;
|
||||
[[nodiscard]] size_t operator()(const char *s) const { return std::hash<std::string_view>{}(s); }
|
||||
[[nodiscard]] size_t operator()(std::string_view s) const { return std::hash<std::string_view>{}(s); }
|
||||
[[nodiscard]] size_t operator()(const std::string &s) const { return std::hash<std::string>{}(s); }
|
||||
};
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, utils::Gatekeeper<T>, string_hash, std::equal_to<>>
|
||||
items_; //!< map to all active items
|
||||
/**
|
||||
* @brief Lower level handler that hides some ugly code.
|
||||
*
|
||||
* @tparam T1 Variadic template of context constructor arguments
|
||||
* @tparam T2 Variadic template of config constructor arguments
|
||||
* @tparam I1 List of indexes associated with the first tuple
|
||||
* @tparam I2 List of indexes associated with the second tuple
|
||||
*/
|
||||
template <typename... T1, typename... T2, std::size_t... I1, std::size_t... I2>
|
||||
NewResult New_(std::string name, std::tuple<T1...> &args1, std::tuple<T2...> &args2,
|
||||
std::integer_sequence<std::size_t, I1...> /*not-used*/,
|
||||
std::integer_sequence<std::size_t, I2...> /*not-used*/) {
|
||||
// Make sure the emplace will succeed, since we don't want to create temporary objects that could break something
|
||||
if (!Has(name)) {
|
||||
auto [itr, _] = items_.emplace(std::piecewise_construct, std::forward_as_tuple(name),
|
||||
std::forward_as_tuple(TConfig{std::forward<T1>(std::get<I1>(args1))...},
|
||||
std::forward<T2>(std::get<I2>(args2))...));
|
||||
return itr->second.get();
|
||||
}
|
||||
spdlog::info("Item with name \"{}\" already exists.", name);
|
||||
return NewError::EXISTS;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, utils::SyncPtr<TContext, TConfig>> items_; //!< map to all active items
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
106
src/dbms/interp_handler.hpp
Normal file
106
src/dbms/interp_handler.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "global.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/config.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/**
|
||||
* @brief Simple class that adds useful information to the query's InterpreterContext
|
||||
*
|
||||
* @tparam T Multi-database handler type
|
||||
*/
|
||||
template <typename T>
|
||||
class ExpandedInterpContext : public query::InterpreterContext {
|
||||
public:
|
||||
template <typename... TArgs>
|
||||
explicit ExpandedInterpContext(T &ref, TArgs &&...args)
|
||||
: query::InterpreterContext(std::forward<TArgs>(args)...), sc_handler_(ref) {}
|
||||
|
||||
T &sc_handler_; //!< Multi-database/SessionContext handler (used in some queries)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple structure that expands on the query's InterpreterConfig
|
||||
*
|
||||
*/
|
||||
struct ExpandedInterpConfig {
|
||||
storage::Config storage_config; //!< Storage configuration
|
||||
query::InterpreterConfig interp_config; //!< Interpreter configuration
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Multi-database interpreter context handler
|
||||
*
|
||||
* @tparam TSCHandler High-level multi-database/SessionContext handler type
|
||||
*/
|
||||
template <typename TSCHandler>
|
||||
class InterpContextHandler : public Handler<ExpandedInterpContext<TSCHandler>, ExpandedInterpConfig> {
|
||||
public:
|
||||
using InterpContextT = ExpandedInterpContext<TSCHandler>;
|
||||
using HandlerT = Handler<InterpContextT, ExpandedInterpConfig>;
|
||||
|
||||
/**
|
||||
* @brief Generate a new interpreter context associated with the passed name.
|
||||
*
|
||||
* @param name Name associating the new interpreter context
|
||||
* @param sc_handler Multi-database/SessionContext handler used (some queries might use it)
|
||||
* @param db Storage associated with the interpreter context
|
||||
* @param config Interpreter's configuration
|
||||
* @param dir Directory used by the interpreter
|
||||
* @param auth_handler AuthQueryHandler used
|
||||
* @param auth_checker AuthChecker used
|
||||
* @return HandlerT::NewResult
|
||||
*/
|
||||
typename HandlerT::NewResult New(const std::string &name, TSCHandler &sc_handler, storage::Config storage_config,
|
||||
const query::InterpreterConfig &interpreter_config,
|
||||
query::AuthQueryHandler &auth_handler, query::AuthChecker &auth_checker) {
|
||||
// Check if compatible with the existing interpreters
|
||||
if (std::any_of(HandlerT::cbegin(), HandlerT::cend(), [&](const auto &elem) {
|
||||
const auto &config = elem.second.config().storage_config;
|
||||
return config.durability.storage_directory == storage_config.durability.storage_directory;
|
||||
})) {
|
||||
spdlog::info("Tried to generate a new context using claimed directory and/or storage.");
|
||||
return NewError::EXISTS;
|
||||
}
|
||||
const auto dir = storage_config.durability.storage_directory;
|
||||
storage_config.name = name; // Set storage id via config
|
||||
return HandlerT::New(
|
||||
name, std::forward_as_tuple(storage_config, interpreter_config),
|
||||
std::forward_as_tuple(sc_handler, storage_config, interpreter_config, dir, &auth_handler, &auth_checker));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief All currently active storage.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
*/
|
||||
std::vector<std::string> All() const {
|
||||
std::vector<std::string> res;
|
||||
res.reserve(std::distance(HandlerT::cbegin(), HandlerT::cend()));
|
||||
std::for_each(HandlerT::cbegin(), HandlerT::cend(), [&](const auto &elem) { res.push_back(elem.first); });
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
#endif
|
||||
61
src/dbms/session_context.hpp
Normal file
61
src/dbms/session_context.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
#if MG_ENTERPRISE
|
||||
#include "audit/log.hpp"
|
||||
#endif
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/**
|
||||
* @brief Structure encapsulating storage and interpreter context.
|
||||
*
|
||||
* @note Each session contains a copy.
|
||||
*/
|
||||
struct SessionContext {
|
||||
// Explicit constructor here to ensure that pointers to all objects are
|
||||
// supplied.
|
||||
|
||||
SessionContext(std::shared_ptr<memgraph::query::InterpreterContext> interpreter_context, std::string run,
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
memgraph::audit::Log *audit_log
|
||||
#endif
|
||||
)
|
||||
: interpreter_context(interpreter_context),
|
||||
run_id(run),
|
||||
auth(auth)
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
audit_log(audit_log)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<memgraph::query::InterpreterContext> interpreter_context;
|
||||
std::string run_id;
|
||||
|
||||
// std::shared_ptr<AuthContext> auth_context;
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::audit::Log *audit_log;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
603
src/dbms/session_context_handler.hpp
Normal file
603
src/dbms/session_context_handler.hpp
Normal file
@@ -0,0 +1,603 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "global.hpp"
|
||||
#include "interp_handler.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/config.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "session_context.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "storage/v2/durability/durability.hpp"
|
||||
#include "storage/v2/durability/paths.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
using DeleteResult = utils::BasicResult<DeleteError>;
|
||||
|
||||
/**
|
||||
* @brief Multi-database session contexts handler.
|
||||
*/
|
||||
class SessionContextHandler {
|
||||
public:
|
||||
using StorageT = storage::Storage;
|
||||
using StorageConfigT = storage::Config;
|
||||
using LockT = utils::RWLock;
|
||||
using NewResultT = utils::BasicResult<NewError, SessionContext>;
|
||||
|
||||
struct Config {
|
||||
StorageConfigT storage_config; //!< Storage configuration
|
||||
query::InterpreterConfig interp_config; //!< Interpreter context configuration
|
||||
std::function<void(utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *,
|
||||
std::unique_ptr<query::AuthQueryHandler> &, std::unique_ptr<query::AuthChecker> &)>
|
||||
glue_auth;
|
||||
};
|
||||
|
||||
struct Statistics {
|
||||
uint64_t num_vertex; //!< Sum of vertexes in every database
|
||||
uint64_t num_edges; //!< Sum of edges in every database
|
||||
uint64_t num_databases; //! number of isolated databases
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize the handler.
|
||||
*
|
||||
* @param audit_log pointer to the audit logger (ENTERPRISE only)
|
||||
* @param configs storage and interpreter configurations
|
||||
* @param recovery_on_startup restore databases (and its content) and authentication data
|
||||
*/
|
||||
SessionContextHandler(memgraph::audit::Log &audit_log, Config configs, bool recovery_on_startup, bool delete_on_drop)
|
||||
: lock_{utils::RWLock::Priority::READ},
|
||||
default_configs_(configs),
|
||||
run_id_{utils::GenerateUUID()},
|
||||
audit_log_(&audit_log),
|
||||
delete_on_drop_(delete_on_drop) {
|
||||
const auto &root = configs.storage_config.durability.storage_directory;
|
||||
utils::EnsureDirOrDie(root);
|
||||
// Verify that the user that started the process is the same user that is
|
||||
// the owner of the storage directory.
|
||||
storage::durability::VerifyStorageDirectoryOwnerAndProcessUserOrDie(root);
|
||||
|
||||
// Create the lock file and open a handle to it. This will crash the
|
||||
// database if it can't open the file for writing or if any other process is
|
||||
// holding the file opened.
|
||||
lock_file_path_ = root / ".lock";
|
||||
lock_file_handle_.Open(lock_file_path_, utils::OutputFile::Mode::OVERWRITE_EXISTING);
|
||||
MG_ASSERT(lock_file_handle_.AcquireLock(),
|
||||
"Couldn't acquire lock on the storage directory {}"
|
||||
"!\nAnother Memgraph process is currently running with the same "
|
||||
"storage directory, please stop it first before starting this "
|
||||
"process!",
|
||||
root);
|
||||
|
||||
// TODO: Figure out if this is needed/wanted
|
||||
// Clear auth database since we are not recovering
|
||||
// if (!recovery_on_startup) {
|
||||
// const auto &auth_dir = root / "auth";
|
||||
// // Backup if auth present
|
||||
// if (utils::DirExists(auth_dir)) {
|
||||
// auto backup_dir = root / storage::durability::kBackupDirectory;
|
||||
// std::error_code error_code;
|
||||
// utils::EnsureDirOrDie(backup_dir);
|
||||
// std::error_code ec;
|
||||
// const auto now = std::chrono::system_clock::now();
|
||||
// std::ostringstream os;
|
||||
// os << now.time_since_epoch().count();
|
||||
// std::filesystem::rename(auth_dir, backup_dir / ("auth-" + os.str()), ec);
|
||||
// MG_ASSERT(!ec, "Couldn't backup auth directory because of: {}", ec.message());
|
||||
// spdlog::warn(
|
||||
// "Since Memgraph was not supposed to recover on startup the authentication files will be "
|
||||
// "overwritten. To prevent important data loss, Memgraph has stored those files into .backup directory "
|
||||
// "inside the storage directory.");
|
||||
// }
|
||||
|
||||
// // Clear
|
||||
// if (std::filesystem::exists(auth_dir)) {
|
||||
// std::filesystem::remove_all(auth_dir);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Lazy initialization of auth_
|
||||
auth_ = std::make_unique<utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock>>(root / "auth");
|
||||
configs.glue_auth(auth_.get(), auth_handler_, auth_checker_);
|
||||
|
||||
// TODO: Decouple storage config from dbms config
|
||||
// TODO: Save individual db configs inside the kvstore and restore from there
|
||||
storage::UpdatePaths(default_configs_->storage_config,
|
||||
default_configs_->storage_config.durability.storage_directory / "databases");
|
||||
const auto &db_dir = default_configs_->storage_config.durability.storage_directory;
|
||||
const auto durability_dir = db_dir / ".durability";
|
||||
utils::EnsureDirOrDie(db_dir);
|
||||
utils::EnsureDirOrDie(durability_dir);
|
||||
durability_ = std::make_unique<kvstore::KVStore>(durability_dir);
|
||||
|
||||
// Generate the default database
|
||||
MG_ASSERT(!NewDefault_().HasError(), "Failed while creating the default DB.");
|
||||
|
||||
// Recover previous databases
|
||||
if (recovery_on_startup) {
|
||||
for (const auto &[name, _] : *durability_) {
|
||||
if (name == kDefaultDB) continue; // Already set
|
||||
spdlog::info("Restoring database {}.", name);
|
||||
MG_ASSERT(!New_(name).HasError(), "Failed while creating database {}.", name);
|
||||
spdlog::info("Database {} restored.", name);
|
||||
}
|
||||
} else { // Clear databases from the durability list and auth
|
||||
auto locked_auth = auth_->Lock();
|
||||
for (const auto &[name, _] : *durability_) {
|
||||
if (name == kDefaultDB) continue;
|
||||
locked_auth->DeleteDatabase(name);
|
||||
durability_->Delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
for (auto &ic : interp_handler_) memgraph::query::Shutdown(ic.second.get().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SessionContext associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New(const std::string &name) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
return New_(name, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the context associated with the "name" database
|
||||
*
|
||||
* @param name
|
||||
* @return SessionContext
|
||||
* @throw UnknownDatabaseException if getting unknown database
|
||||
*/
|
||||
SessionContext Get(const std::string &name) {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return Get_(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the undelying database for a particular session.
|
||||
*
|
||||
* @param uuid unique session identifier
|
||||
* @param db_name unique database name
|
||||
* @return SetForResult enum
|
||||
* @throws UnknownDatabaseException, UnknownSessionException or anything OnChange throws
|
||||
*/
|
||||
SetForResult SetFor(const std::string &uuid, const std::string &db_name) {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
(void)Get_(
|
||||
db_name); // throws if db doesn't exist (TODO: Better to pass it via OnChange - but injecting dependency)
|
||||
try {
|
||||
auto &s = sessions_.at(uuid);
|
||||
return s.OnChange(db_name);
|
||||
} catch (std::out_of_range &) {
|
||||
throw UnknownSessionException("Unknown session \"{}\"", uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the undelying database from a session itself. SessionContext handler.
|
||||
*
|
||||
* @param db_name unique database name
|
||||
* @param handler function that gets called in place with the appropriate SessionContext
|
||||
* @return SetForResult enum
|
||||
*/
|
||||
template <typename THandler>
|
||||
requires std::invocable<THandler, SessionContext> SetForResult SetInPlace(const std::string &db_name,
|
||||
THandler handler) {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return handler(Get_(db_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call void handler under a shared lock.
|
||||
*
|
||||
* @param handler function that gets called in place
|
||||
*/
|
||||
template <typename THandler>
|
||||
requires std::invocable<THandler>
|
||||
void CallInPlace(THandler handler) {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register an active session (used to handle callbacks).
|
||||
*
|
||||
* @param session
|
||||
* @return true on success
|
||||
*/
|
||||
bool Register(SessionInterface &session) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
auto [_, success] = sessions_.emplace(session.UUID(), session);
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete a session.
|
||||
*
|
||||
* @param session
|
||||
*/
|
||||
bool Delete(const SessionInterface &session) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
return sessions_.erase(session.UUID()) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete database.
|
||||
*
|
||||
* @param db_name database name
|
||||
* @return DeleteResult error on failure
|
||||
*/
|
||||
DeleteResult Delete(const std::string &db_name) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
if (db_name == kDefaultDB) {
|
||||
// MSG cannot delete the default db
|
||||
return DeleteError::DEFAULT_DB;
|
||||
}
|
||||
// Check if db exists
|
||||
try {
|
||||
auto sc = Get_(db_name);
|
||||
// Check if a session is using the db
|
||||
if (!sc.interpreter_context->interpreters->empty()) {
|
||||
return DeleteError::USING;
|
||||
}
|
||||
} catch (UnknownDatabaseException &) {
|
||||
return DeleteError::NON_EXISTENT;
|
||||
}
|
||||
|
||||
// High level handlers
|
||||
for (auto &[_, s] : sessions_) {
|
||||
if (!s.OnDelete(db_name)) {
|
||||
spdlog::error("Partial failure while deleting database \"{}\".", db_name);
|
||||
defunct_dbs_.emplace(db_name);
|
||||
return DeleteError::FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Low level handlers
|
||||
const auto storage_path = StorageDir_(db_name);
|
||||
MG_ASSERT(storage_path, "Missing storage for {}", db_name);
|
||||
if (!interp_handler_.Delete(db_name)) {
|
||||
spdlog::error("Partial failure while deleting database \"{}\".", db_name);
|
||||
defunct_dbs_.emplace(db_name);
|
||||
return DeleteError::FAIL;
|
||||
}
|
||||
|
||||
// Remove from auth
|
||||
auth_->Lock()->DeleteDatabase(db_name);
|
||||
// Remove from durability list
|
||||
if (durability_) durability_->Delete(db_name);
|
||||
|
||||
// Delete disk storage
|
||||
if (delete_on_drop_) {
|
||||
std::error_code ec;
|
||||
(void)std::filesystem::remove_all(*storage_path, ec);
|
||||
if (ec) {
|
||||
spdlog::error("Failed to clean disk while deleting database \"{}\".", db_name);
|
||||
defunct_dbs_.emplace(db_name);
|
||||
return DeleteError::DISK_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from defunct_dbs_ (in case a second delete call was successful)
|
||||
defunct_dbs_.erase(db_name);
|
||||
|
||||
return {}; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the default configurations.
|
||||
*
|
||||
* @param configs storage, interpreter and authorization configurations
|
||||
*/
|
||||
void SetDefaultConfigs(Config configs) {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
default_configs_ = configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the default configurations.
|
||||
*
|
||||
* @return std::optional<Config>
|
||||
*/
|
||||
std::optional<Config> GetDefaultConfigs() const {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return default_configs_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return all active databases.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
*/
|
||||
std::vector<std::string> All() const {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return interp_handler_.All();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the number of vertex across all databases.
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
Statistics Info() const {
|
||||
// TODO: Handle overflow
|
||||
uint64_t nv = 0;
|
||||
uint64_t ne = 0;
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
const uint64_t ndb = std::distance(interp_handler_.cbegin(), interp_handler_.cend());
|
||||
for (const auto &ic : interp_handler_) {
|
||||
const auto &info = ic.second.get()->db->GetInfo();
|
||||
nv += info.vertex_count;
|
||||
ne += info.edge_count;
|
||||
}
|
||||
return {nv, ne, ndb};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the currently active database for a particular session.
|
||||
*
|
||||
* @param uuid session's unique identifier
|
||||
* @return std::string name of the database
|
||||
* @throw
|
||||
*/
|
||||
std::string Current(const std::string &uuid) const {
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return sessions_.at(uuid).GetDatabaseName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore triggers for all currently defined databases.
|
||||
* @note: Triggers can execute query procedures, so we need to reload the modules first and then the triggers
|
||||
*/
|
||||
void RestoreTriggers() {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
for (auto &ic_itr : interp_handler_) {
|
||||
auto ic = ic_itr.second.get();
|
||||
spdlog::debug("Restoring trigger for database \"{}\"", ic->db->id());
|
||||
auto storage_accessor = ic->db->Access();
|
||||
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
|
||||
ic->trigger_store.RestoreTriggers(&ic->ast_cache, &dba, ic->config.query, ic->auth_checker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore streams of all currently defined databases.
|
||||
* @note: Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
||||
*/
|
||||
void RestoreStreams() {
|
||||
std::lock_guard<LockT> wr(lock_);
|
||||
for (auto &ic_itr : interp_handler_) {
|
||||
auto ic = ic_itr.second.get();
|
||||
spdlog::debug("Restoring streams for database \"{}\"", ic->db->id());
|
||||
ic->streams.RestoreStreams();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<std::filesystem::path> StorageDir_(const std::string &name) const {
|
||||
const auto conf = interp_handler_.GetConfig(name);
|
||||
if (conf) {
|
||||
return conf->storage_config.durability.storage_directory;
|
||||
}
|
||||
spdlog::debug("Failed to find storage dir for database \"{}\"", name);
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SessionContext associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name) { return New_(name, name); }
|
||||
|
||||
/**
|
||||
* @brief Create a new SessionContext associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @param storage_subdir undelying RocksDB directory
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name, std::filesystem::path storage_subdir) {
|
||||
if (default_configs_) {
|
||||
auto storage = default_configs_->storage_config;
|
||||
storage::UpdatePaths(storage, storage.durability.storage_directory / storage_subdir);
|
||||
return New_(name, storage, default_configs_->interp_config);
|
||||
}
|
||||
spdlog::info("Trying to generate session context without any configurations.");
|
||||
return NewError::NO_CONFIGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SessionContext associated with the "name" database
|
||||
*
|
||||
* @param name name of the database
|
||||
* @param storage_config storage configuration
|
||||
* @param inter_config interpreter configuration
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT New_(const std::string &name, StorageConfigT &storage_config, query::InterpreterConfig &inter_config/*,
|
||||
const std::string &ah_flags*/) {
|
||||
MG_ASSERT(auth_handler_, "No high level AuthQueryHandler has been supplied.");
|
||||
MG_ASSERT(auth_checker_, "No high level AuthChecker has been supplied.");
|
||||
|
||||
if (defunct_dbs_.contains(name)) {
|
||||
spdlog::warn("Failed to generate database due to the unknown state of the previously defunct database \"{}\".",
|
||||
name);
|
||||
return NewError::DEFUNCT;
|
||||
}
|
||||
|
||||
auto new_interp = interp_handler_.New(name, *this, storage_config, inter_config, *auth_handler_, *auth_checker_);
|
||||
|
||||
if (new_interp.HasValue()) {
|
||||
// Success
|
||||
if (durability_) durability_->Put(name, "ok");
|
||||
return SessionContext{new_interp.GetValue(), run_id_, auth_.get(), audit_log_};
|
||||
}
|
||||
return new_interp.GetError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SessionContext associated with the default database
|
||||
*
|
||||
* @return NewResultT context on success, error on failure
|
||||
*/
|
||||
NewResultT NewDefault_() {
|
||||
// Create the default DB in the root (this is how it was done pre multi-tenancy)
|
||||
auto res = New_(kDefaultDB, "..");
|
||||
if (res.HasValue()) {
|
||||
// For back-compatibility...
|
||||
// Recreate the dbms layout for the default db and symlink to the root
|
||||
const auto dir = StorageDir_(kDefaultDB);
|
||||
MG_ASSERT(dir, "Failed to find storage path.");
|
||||
const auto main_dir = *dir / "databases" / kDefaultDB;
|
||||
|
||||
if (!std::filesystem::exists(main_dir)) {
|
||||
std::filesystem::create_directory(main_dir);
|
||||
}
|
||||
|
||||
// Force link on-disk directories
|
||||
const auto conf = interp_handler_.GetConfig(kDefaultDB);
|
||||
MG_ASSERT(conf, "No configuration for the default database.");
|
||||
const auto &tmp_conf = conf->storage_config.disk;
|
||||
std::vector<std::filesystem::path> to_link{
|
||||
tmp_conf.main_storage_directory, tmp_conf.label_index_directory,
|
||||
tmp_conf.label_property_index_directory, tmp_conf.unique_constraints_directory,
|
||||
tmp_conf.name_id_mapper_directory, tmp_conf.id_name_mapper_directory,
|
||||
tmp_conf.durability_directory, tmp_conf.wal_directory,
|
||||
};
|
||||
|
||||
// Add in-memory paths
|
||||
// Some directories are redundant (skip those)
|
||||
const std::vector<std::string> skip{".lock", "audit_log", "auth", "databases", "internal_modules", "settings"};
|
||||
for (auto const &item : std::filesystem::directory_iterator{*dir}) {
|
||||
const auto dir_name = std::filesystem::relative(item.path(), item.path().parent_path());
|
||||
if (std::find(skip.begin(), skip.end(), dir_name) != skip.end()) continue;
|
||||
to_link.push_back(item.path());
|
||||
}
|
||||
|
||||
// Symlink to root dir
|
||||
for (auto const &item : to_link) {
|
||||
const auto dir_name = std::filesystem::relative(item, item.parent_path());
|
||||
const auto link = main_dir / dir_name;
|
||||
const auto to = std::filesystem::relative(item, main_dir);
|
||||
if (!std::filesystem::is_symlink(link) && !std::filesystem::exists(link)) {
|
||||
std::filesystem::create_directory_symlink(to, link);
|
||||
} else { // Check existing link
|
||||
std::error_code ec;
|
||||
const auto test_link = std::filesystem::read_symlink(link, ec);
|
||||
if (ec || test_link != to) {
|
||||
MG_ASSERT(false,
|
||||
"Memgraph storage directory incompatible with new version.\n"
|
||||
"Please use a clean directory or remove \"{}\" and try again.",
|
||||
link.string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the context associated with the "name" database
|
||||
*
|
||||
* @param name
|
||||
* @return SessionContext
|
||||
* @throw UnknownDatabaseException if trying to get unknown database
|
||||
*/
|
||||
SessionContext Get_(const std::string &name) {
|
||||
auto interp = interp_handler_.Get(name);
|
||||
if (interp) {
|
||||
return SessionContext{*interp, run_id_, auth_.get(), audit_log_};
|
||||
}
|
||||
throw UnknownDatabaseException("Tried to retrieve an unknown database \"{}\".", name);
|
||||
}
|
||||
|
||||
// Should storage objects ever be deleted?
|
||||
mutable LockT lock_; //!< protective lock
|
||||
std::filesystem::path lock_file_path_; //!< Lock file protecting the main storage
|
||||
utils::OutputFile lock_file_handle_; //!< Handler the lock (crash if already open)
|
||||
InterpContextHandler<SessionContextHandler> interp_handler_; //!< multi-tenancy interpreter handler
|
||||
// AuthContextHandler auth_handler_; //!< multi-tenancy authorization handler (currently we use a single global
|
||||
// auth)
|
||||
std::unique_ptr<utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock>> auth_;
|
||||
std::unique_ptr<query::AuthQueryHandler> auth_handler_;
|
||||
std::unique_ptr<query::AuthChecker> auth_checker_;
|
||||
std::optional<Config> default_configs_; //!< default storage and interpreter configurations
|
||||
const std::string run_id_; //!< run's unique identifier (auto generated)
|
||||
memgraph::audit::Log *audit_log_; //!< pointer to the audit logger
|
||||
std::unordered_map<std::string, SessionInterface &> sessions_; //!< map of active/registered sessions
|
||||
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
|
||||
|
||||
std::set<std::string> defunct_dbs_; //!< Databases that are in an unknown state due to various failures
|
||||
bool delete_on_drop_; //!< Flag defining if dropping storage also deletes its directory
|
||||
public:
|
||||
static SessionContextHandler &ExtractSCH(query::InterpreterContext *interpreter_context) {
|
||||
return static_cast<typename decltype(interp_handler_)::InterpContextT *>(interpreter_context)->sc_handler_;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
/**
|
||||
* @brief Initialize the handler.
|
||||
*
|
||||
* @param auth pointer to the authenticator
|
||||
* @param configs storage and interpreter configurations
|
||||
*/
|
||||
static inline SessionContext Init(storage::Config &storage_config, query::InterpreterConfig &interp_config,
|
||||
utils::Synchronized<auth::Auth, utils::WritePrioritizedRWLock> *auth,
|
||||
query::AuthQueryHandler *auth_handler, query::AuthChecker *auth_checker) {
|
||||
MG_ASSERT(auth, "Passed a nullptr auth");
|
||||
MG_ASSERT(auth_handler, "Passed a nullptr auth_handler");
|
||||
MG_ASSERT(auth_checker, "Passed a nullptr auth_checker");
|
||||
|
||||
storage_config.name = kDefaultDB;
|
||||
auto interp_context = std::make_shared<query::InterpreterContext>(
|
||||
storage_config, interp_config, storage_config.durability.storage_directory, auth_handler, auth_checker);
|
||||
MG_ASSERT(interp_context, "Failed to construct main interpret context.");
|
||||
|
||||
return SessionContext{interp_context, utils::GenerateUUID(), auth};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
@@ -1,4 +0,0 @@
|
||||
add_library(mg-distributed)
|
||||
add_library(mg::distributed ALIAS mg-distributed)
|
||||
target_include_directories(mg-distributed PUBLIC include )
|
||||
target_sources(mg-distributed PRIVATE lamport_clock.cpp)
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
|
||||
namespace memgraph::distributed {
|
||||
|
||||
// forward declare, for strong timestamps
|
||||
template <typename Tag>
|
||||
struct LamportClock;
|
||||
|
||||
template <typename Tag>
|
||||
struct timestamp {
|
||||
friend std::strong_ordering operator<=>(timestamp const &, timestamp const &) = default;
|
||||
|
||||
private:
|
||||
friend struct LamportClock<Tag>;
|
||||
|
||||
explicit timestamp(uint64_t value) : value_{value} {}
|
||||
uint64_t value_;
|
||||
};
|
||||
|
||||
constexpr struct internal_t {
|
||||
} internal;
|
||||
constexpr struct send_t {
|
||||
} send;
|
||||
constexpr struct receive_t {
|
||||
} receive;
|
||||
|
||||
template <typename Tag>
|
||||
struct LamportClock {
|
||||
using timestamp_t = timestamp<Tag>;
|
||||
|
||||
auto get_timestamp(internal_t) -> timestamp_t { return timestamp_t{++internal}; };
|
||||
auto get_timestamp(send_t) -> timestamp_t { return timestamp_t{++internal}; };
|
||||
auto get_timestamp(receive_t, timestamp_t received_timestamp) -> timestamp_t {
|
||||
while (true) {
|
||||
auto local_current = internal.load(std::memory_order_acquire);
|
||||
auto next = std::max(received_timestamp.value_, local_current) + 1;
|
||||
bool res = internal.compare_exchange_weak(local_current, next, std::memory_order_acq_rel);
|
||||
if (res) return timestamp_t{next};
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<uint64_t> internal = 0;
|
||||
};
|
||||
|
||||
} // namespace memgraph::distributed
|
||||
@@ -1,11 +0,0 @@
|
||||
// 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 "distributed/lamport_clock.hpp"
|
||||
@@ -1,10 +0,0 @@
|
||||
add_library(mg-flags STATIC audit.cpp
|
||||
bolt.cpp
|
||||
general.cpp
|
||||
isolation_level.cpp
|
||||
log_level.cpp
|
||||
memory_limit.cpp
|
||||
run_time_configurable.cpp
|
||||
storage_mode.cpp)
|
||||
target_include_directories(mg-flags PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-flags PUBLIC spdlog::spdlog mg-settings mg-utils)
|
||||
@@ -16,5 +16,3 @@
|
||||
#include "flags/isolation_level.hpp"
|
||||
#include "flags/log_level.hpp"
|
||||
#include "flags/memory_limit.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "flags/storage_mode.hpp"
|
||||
|
||||
@@ -36,7 +36,3 @@ DEFINE_VALIDATED_int32(bolt_session_inactivity_timeout, 1800,
|
||||
DEFINE_string(bolt_cert_file, "", "Certificate file which should be used for the Bolt server.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(bolt_key_file, "", "Key file which should be used for the Bolt server.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(bolt_server_name_for_init, "",
|
||||
"Server name which the database should send to the client in the "
|
||||
"Bolt INIT message.");
|
||||
|
||||
@@ -25,5 +25,3 @@ DECLARE_int32(bolt_session_inactivity_timeout);
|
||||
DECLARE_string(bolt_cert_file);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(bolt_key_file);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(bolt_server_name_for_init);
|
||||
|
||||
@@ -11,13 +11,9 @@
|
||||
|
||||
#include "general.hpp"
|
||||
|
||||
#include "glue/auth_global.hpp"
|
||||
#include "storage/v2/config.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include "glue/auth_handler.hpp"
|
||||
|
||||
// Short help flag.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -146,7 +142,7 @@ DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Puls
|
||||
// Query flags.
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_double(query_execution_timeout_sec, -1,
|
||||
DEFINE_double(query_execution_timeout_sec, 600,
|
||||
"Maximum allowed query execution time. Queries exceeding this "
|
||||
"limit will be aborted. Value of 0 means no limit.");
|
||||
|
||||
|
||||
@@ -37,10 +37,7 @@ inline constexpr std::array log_level_mappings{
|
||||
const std::string log_level_help_string = fmt::format("Minimum log level. Allowed values: {}",
|
||||
memgraph::utils::GetAllowedEnumValuesString(log_level_mappings));
|
||||
|
||||
DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(),
|
||||
{ return memgraph::flags::ValidLogLevel(value); });
|
||||
|
||||
bool memgraph::flags::ValidLogLevel(std::string_view value) {
|
||||
DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(), {
|
||||
if (const auto result = memgraph::utils::IsValidEnumValueString(value, log_level_mappings); result.HasError()) {
|
||||
const auto error = result.GetError();
|
||||
switch (error) {
|
||||
@@ -58,14 +55,10 @@ bool memgraph::flags::ValidLogLevel(std::string_view value) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<spdlog::level::level_enum> memgraph::flags::LogLevelToEnum(std::string_view value) {
|
||||
return memgraph::utils::StringToEnum<spdlog::level::level_enum>(value, log_level_mappings);
|
||||
}
|
||||
});
|
||||
|
||||
spdlog::level::level_enum ParseLogLevel() {
|
||||
const auto log_level = memgraph::flags::LogLevelToEnum(FLAGS_log_level);
|
||||
const auto log_level = memgraph::utils::StringToEnum<spdlog::level::level_enum>(FLAGS_log_level, log_level_mappings);
|
||||
MG_ASSERT(log_level, "Invalid log level");
|
||||
return *log_level;
|
||||
}
|
||||
@@ -77,19 +70,14 @@ void CreateLoggerFromSink(const auto &sinks, const auto log_level) {
|
||||
logger->set_level(log_level);
|
||||
logger->flush_on(spdlog::level::trace);
|
||||
spdlog::set_default_logger(std::move(logger));
|
||||
// Enable stderr sink
|
||||
if (FLAGS_also_log_to_stderr) {
|
||||
memgraph::flags::LogToStderr(log_level);
|
||||
}
|
||||
}
|
||||
|
||||
void memgraph::flags::InitializeLogger() {
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
|
||||
// Force the stderr logger to be at the front of the sinks vector
|
||||
// Will be used to disable/enable it at run-time by settings its log level
|
||||
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
|
||||
sinks.back()->set_level(spdlog::level::off);
|
||||
if (FLAGS_also_log_to_stderr) {
|
||||
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
|
||||
}
|
||||
|
||||
if (!FLAGS_log_file.empty()) {
|
||||
// get local time
|
||||
@@ -105,18 +93,9 @@ void memgraph::flags::InitializeLogger() {
|
||||
CreateLoggerFromSink(sinks, ParseLogLevel());
|
||||
}
|
||||
|
||||
// TODO: Make sure this is used in a safe way
|
||||
void memgraph::flags::AddLoggerSink(spdlog::sink_ptr new_sink) {
|
||||
auto default_logger = spdlog::default_logger();
|
||||
auto sinks = default_logger->sinks();
|
||||
sinks.push_back(new_sink);
|
||||
CreateLoggerFromSink(sinks, default_logger->level());
|
||||
}
|
||||
|
||||
// Thread-safe because the level enum is an atomic
|
||||
// NOTE: default_logger is not thread-safe and shouldn't be changed during application lifetime
|
||||
void memgraph::flags::LogToStderr(spdlog::level::level_enum log_level) {
|
||||
auto default_logger = spdlog::default_logger();
|
||||
auto sink = default_logger->sinks().front();
|
||||
sink->set_level(log_level);
|
||||
}
|
||||
|
||||
@@ -11,19 +11,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <optional>
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
DECLARE_string(log_level);
|
||||
DECLARE_bool(also_log_to_stderr);
|
||||
|
||||
namespace memgraph::flags {
|
||||
|
||||
bool ValidLogLevel(std::string_view value);
|
||||
std::optional<spdlog::level::level_enum> LogLevelToEnum(std::string_view value);
|
||||
|
||||
void InitializeLogger();
|
||||
void AddLoggerSink(spdlog::sink_ptr new_sink);
|
||||
void LogToStderr(spdlog::level::level_enum log_level);
|
||||
|
||||
} // namespace memgraph::flags
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
// 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 "flags/run_time_configurable.hpp"
|
||||
#include <string>
|
||||
#include "flags/bolt.hpp"
|
||||
#include "flags/general.hpp"
|
||||
#include "flags/log_level.hpp"
|
||||
#include "spdlog/cfg/helpers-inl.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace {
|
||||
// Bolt server name
|
||||
constexpr auto kServerNameSettingKey = "server.name";
|
||||
constexpr auto kDefaultServerName = "Neo4j/v5.11.0 compatible graph database server - Memgraph";
|
||||
// Query timeout
|
||||
constexpr auto kQueryTxSettingKey = "query.timeout";
|
||||
constexpr auto kDefaultQueryTx = "600"; // seconds
|
||||
// Log level
|
||||
// No default value because it is not persistent
|
||||
constexpr auto kLogLevelSettingKey = "log.level";
|
||||
// Log to stderr
|
||||
// No default value because it is not persistent
|
||||
constexpr auto kLogToStderrSettingKey = "log.to_stderr";
|
||||
} // namespace
|
||||
|
||||
namespace memgraph::flags::run_time {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
memgraph::utils::Synchronized<std::string, memgraph::utils::SpinLock> bolt_server_name_;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::atomic<double> execution_timeout_sec_;
|
||||
|
||||
void Initialize() {
|
||||
// Register bolt server name settings
|
||||
memgraph::utils::global_settings.RegisterSetting(kServerNameSettingKey, kDefaultServerName, [&] {
|
||||
const auto server_name = memgraph::utils::global_settings.GetValue(kServerNameSettingKey);
|
||||
MG_ASSERT(server_name, "Bolt server name is missing from the settings");
|
||||
*(bolt_server_name_.Lock()) = *server_name;
|
||||
});
|
||||
// Update value from read settings
|
||||
const auto &name = memgraph::utils::global_settings.GetValue(kServerNameSettingKey);
|
||||
MG_ASSERT(name, "Failed to read server name from settings.");
|
||||
*(bolt_server_name_.Lock()) = *name;
|
||||
// Override server name if passed via command line argument
|
||||
if (!FLAGS_bolt_server_name_for_init.empty()) {
|
||||
memgraph::utils::global_settings.SetValue(kServerNameSettingKey, FLAGS_bolt_server_name_for_init);
|
||||
}
|
||||
|
||||
// Register query timeout
|
||||
memgraph::utils::global_settings.RegisterSetting(kQueryTxSettingKey, kDefaultQueryTx, [&] {
|
||||
const auto query_tx = memgraph::utils::global_settings.GetValue(kQueryTxSettingKey);
|
||||
MG_ASSERT(query_tx, "Query timeout is missing from the settings");
|
||||
execution_timeout_sec_ = std::stod(*query_tx);
|
||||
});
|
||||
// Update value from read settings
|
||||
const auto &tx = memgraph::utils::global_settings.GetValue(kQueryTxSettingKey);
|
||||
MG_ASSERT(tx, "Failed to read query timeout from settings.");
|
||||
execution_timeout_sec_ = std::stod(*tx);
|
||||
// Override query timeout if passed via command line argument
|
||||
if (FLAGS_query_execution_timeout_sec != -1) {
|
||||
memgraph::utils::global_settings.SetValue(kQueryTxSettingKey, std::to_string(FLAGS_query_execution_timeout_sec));
|
||||
}
|
||||
|
||||
// Register log level
|
||||
auto get_global_log_level = []() {
|
||||
const auto log_level = memgraph::utils::global_settings.GetValue(kLogLevelSettingKey);
|
||||
MG_ASSERT(log_level, "Log level is missing from the settings");
|
||||
const auto ll_enum = memgraph::flags::LogLevelToEnum(*log_level);
|
||||
if (!ll_enum) {
|
||||
throw utils::BasicException("Unsupported log level {}", *log_level);
|
||||
}
|
||||
return *ll_enum;
|
||||
};
|
||||
memgraph::utils::global_settings.RegisterSetting(
|
||||
kLogLevelSettingKey, FLAGS_log_level, [&] { spdlog::set_level(get_global_log_level()); },
|
||||
memgraph::flags::ValidLogLevel);
|
||||
// Always override log level with command line argument
|
||||
memgraph::utils::global_settings.SetValue(kLogLevelSettingKey, FLAGS_log_level);
|
||||
|
||||
// Register logging to stderr
|
||||
auto bool_to_str = [](bool in) { return in ? "true" : "false"; };
|
||||
const std::string log_to_stderr_s = bool_to_str(FLAGS_also_log_to_stderr);
|
||||
memgraph::utils::global_settings.RegisterSetting(
|
||||
kLogToStderrSettingKey, log_to_stderr_s,
|
||||
[&] {
|
||||
const auto enable = memgraph::utils::global_settings.GetValue(kLogToStderrSettingKey);
|
||||
if (enable == "true") {
|
||||
LogToStderr(get_global_log_level());
|
||||
} else {
|
||||
LogToStderr(spdlog::level::off);
|
||||
}
|
||||
},
|
||||
[](std::string_view in) {
|
||||
const auto lc = memgraph::utils::ToLowerCase(in);
|
||||
return lc == "false" || lc == "true";
|
||||
});
|
||||
// Always override log to stderr with command line argument
|
||||
memgraph::utils::global_settings.SetValue(kLogToStderrSettingKey, log_to_stderr_s);
|
||||
}
|
||||
} // namespace memgraph::flags::run_time
|
||||
@@ -1,26 +0,0 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace memgraph::flags::run_time {
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
extern utils::Synchronized<std::string, utils::SpinLock> bolt_server_name_;
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
extern std::atomic<double> execution_timeout_sec_;
|
||||
|
||||
void Initialize();
|
||||
|
||||
} // namespace memgraph::flags::run_time
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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 "flags/storage_mode.hpp"
|
||||
|
||||
#include "storage/v2/storage_mode.hpp"
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
inline constexpr std::array storage_mode_mappings{
|
||||
std::pair{std::string_view{"IN_MEMORY_TRANSACTIONAL"}, memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL},
|
||||
std::pair{std::string_view{"IN_MEMORY_ANALYTICAL"}, memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL},
|
||||
std::pair{std::string_view{"ON_DISK_TRANSACTIONAL"}, memgraph::storage::StorageMode::ON_DISK_TRANSACTIONAL}};
|
||||
|
||||
const std::string storage_mode_help_string =
|
||||
fmt::format("Default storage mode Memgraph uses. Allowed values: {}",
|
||||
memgraph::utils::GetAllowedEnumValuesString(storage_mode_mappings));
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_string(storage_mode, "IN_MEMORY_TRANSACTIONAL", storage_mode_help_string.c_str(), {
|
||||
if (const auto result = memgraph::utils::IsValidEnumValueString(value, storage_mode_mappings); result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
case memgraph::utils::ValidationError::EmptyValue: {
|
||||
std::cout << "Storage mode cannot be empty." << std::endl;
|
||||
break;
|
||||
}
|
||||
case memgraph::utils::ValidationError::InvalidValue: {
|
||||
std::cout << "Invalid value for storage mode. Allowed values: "
|
||||
<< memgraph::utils::GetAllowedEnumValuesString(storage_mode_mappings) << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
memgraph::storage::StorageMode memgraph::flags::ParseStorageMode() {
|
||||
const auto storage_mode =
|
||||
memgraph::utils::StringToEnum<memgraph::storage::StorageMode>(FLAGS_storage_mode, storage_mode_mappings);
|
||||
MG_ASSERT(storage_mode, "Invalid storage mode");
|
||||
return *storage_mode;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "storage/v2/storage_mode.hpp"
|
||||
|
||||
namespace memgraph::flags {
|
||||
|
||||
memgraph::storage::StorageMode ParseStorageMode();
|
||||
|
||||
} // namespace memgraph::flags
|
||||
@@ -1,11 +1,4 @@
|
||||
add_library(mg-glue STATIC )
|
||||
target_sources(mg-glue PRIVATE auth.cpp
|
||||
auth_checker.cpp
|
||||
auth_handler.cpp
|
||||
communication.cpp
|
||||
SessionHL.cpp
|
||||
ServerT.cpp
|
||||
MonitoringServerT.cpp
|
||||
run_id.cpp)
|
||||
target_sources(mg-glue PRIVATE auth.cpp auth_checker.cpp auth_handler.cpp communication.cpp SessionHL.cpp ServerT.cpp MonitoringServerT.cpp)
|
||||
target_link_libraries(mg-glue mg-query mg-auth mg-audit)
|
||||
target_precompile_headers(mg-glue INTERFACE auth_checker.hpp auth_handler.hpp)
|
||||
|
||||
@@ -10,4 +10,5 @@
|
||||
// licenses/APL.txt.
|
||||
#include "glue/MonitoringServerT.hpp"
|
||||
|
||||
template class memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler, memgraph::storage::Storage>;
|
||||
template class memgraph::communication::http::Server<
|
||||
memgraph::http::MetricsRequestHandler<memgraph::dbms::SessionContext>, memgraph::dbms::SessionContext>;
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "communication/http/server.hpp"
|
||||
#include "dbms/session_context.hpp"
|
||||
#include "http_handlers/metrics.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
extern template class memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler,
|
||||
memgraph::storage::Storage>;
|
||||
extern template class memgraph::communication::http::Server<
|
||||
memgraph::http::MetricsRequestHandler<memgraph::dbms::SessionContext>, memgraph::dbms::SessionContext>;
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
using MonitoringServerT =
|
||||
memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler, memgraph::storage::Storage>;
|
||||
memgraph::communication::http::Server<memgraph::http::MetricsRequestHandler<memgraph::dbms::SessionContext>,
|
||||
memgraph::dbms::SessionContext>;
|
||||
} // namespace memgraph::glue
|
||||
|
||||
@@ -10,4 +10,8 @@
|
||||
// licenses/APL.txt.
|
||||
#include "glue/ServerT.hpp"
|
||||
|
||||
template class memgraph::communication::v2::Server<memgraph::glue::SessionHL, Context>;
|
||||
#ifdef MG_ENTERPRISE
|
||||
template class memgraph::communication::v2::Server<memgraph::glue::SessionHL, memgraph::dbms::SessionContextHandler>;
|
||||
#else
|
||||
template class memgraph::communication::v2::Server<memgraph::glue::SessionHL, memgraph::dbms::SessionContext>;
|
||||
#endif
|
||||
|
||||
@@ -12,35 +12,24 @@
|
||||
|
||||
#include "communication/v2/server.hpp"
|
||||
#include "glue/SessionHL.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
struct InterpreterContext;
|
||||
}
|
||||
|
||||
#if MG_ENTERPRISE
|
||||
namespace memgraph::audit {
|
||||
class Log;
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "dbms/session_context_handler.hpp"
|
||||
#else
|
||||
#include "dbms/session_context.hpp"
|
||||
#endif
|
||||
|
||||
namespace memgraph::auth {
|
||||
class Auth;
|
||||
}
|
||||
namespace memgraph::utils {
|
||||
class WritePrioritizedRWLock;
|
||||
}
|
||||
|
||||
struct Context {
|
||||
memgraph::query::InterpreterContext *ic;
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
|
||||
#if MG_ENTERPRISE
|
||||
memgraph::audit::Log *audit_log;
|
||||
#ifdef MG_ENTERPRISE
|
||||
extern template class memgraph::communication::v2::Server<memgraph::glue::SessionHL,
|
||||
memgraph::dbms::SessionContextHandler>;
|
||||
#else
|
||||
extern template class memgraph::communication::v2::Server<memgraph::glue::SessionHL, memgraph::dbms::SessionContext>;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern template class memgraph::communication::v2::Server<memgraph::glue::SessionHL, Context>;
|
||||
|
||||
namespace memgraph::glue {
|
||||
using ServerT = memgraph::communication::v2::Server<memgraph::glue::SessionHL, Context>;
|
||||
#ifdef MG_ENTERPRISE
|
||||
using ServerT = memgraph::communication::v2::Server<memgraph::glue::SessionHL, memgraph::dbms::SessionContextHandler>;
|
||||
#else
|
||||
using ServerT = memgraph::communication::v2::Server<memgraph::glue::SessionHL, memgraph::dbms::SessionContext>;
|
||||
#endif
|
||||
} // namespace memgraph::glue
|
||||
|
||||
@@ -9,25 +9,25 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include <optional>
|
||||
#include "gflags/gflags.h"
|
||||
#include "glue/SessionHL.hpp"
|
||||
|
||||
#include "audit/log.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "glue/SessionHL.hpp"
|
||||
#include "glue/auth_checker.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#include "glue/run_id.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
namespace memgraph::metrics {
|
||||
extern const Event ActiveBoltSessions;
|
||||
} // namespace memgraph::metrics
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(bolt_server_name_for_init, "",
|
||||
"Server name which the database should send to the client in the "
|
||||
"Bolt INIT message.");
|
||||
|
||||
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
|
||||
auto const &as_map = extra.ValueMap();
|
||||
|
||||
@@ -49,14 +49,14 @@ auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgrap
|
||||
|
||||
class TypedValueResultStreamBase {
|
||||
public:
|
||||
explicit TypedValueResultStreamBase(memgraph::storage::Storage *storage);
|
||||
explicit TypedValueResultStreamBase(memgraph::query::InterpreterContext *interpreterContext);
|
||||
|
||||
std::vector<memgraph::communication::bolt::Value> DecodeValues(
|
||||
const std::vector<memgraph::query::TypedValue> &values) const;
|
||||
|
||||
protected:
|
||||
private:
|
||||
// NOTE: Needed only for ToBoltValue conversions
|
||||
memgraph::storage::Storage *storage_;
|
||||
memgraph::query::InterpreterContext *interpreter_context_;
|
||||
};
|
||||
|
||||
/// Wrapper around TEncoder which converts TypedValue to Value
|
||||
@@ -64,8 +64,8 @@ class TypedValueResultStreamBase {
|
||||
template <typename TEncoder>
|
||||
class TypedValueResultStream : public TypedValueResultStreamBase {
|
||||
public:
|
||||
TypedValueResultStream(TEncoder *encoder, memgraph::storage::Storage *storage)
|
||||
: TypedValueResultStreamBase{storage}, encoder_(encoder) {}
|
||||
TypedValueResultStream(TEncoder *encoder, memgraph::query::InterpreterContext *ic)
|
||||
: TypedValueResultStreamBase{ic}, encoder_(encoder) {}
|
||||
|
||||
void Result(const std::vector<memgraph::query::TypedValue> &values) { encoder_->MessageRecord(DecodeValues(values)); }
|
||||
|
||||
@@ -78,7 +78,7 @@ std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::De
|
||||
std::vector<memgraph::communication::bolt::Value> decoded_values;
|
||||
decoded_values.reserve(values.size());
|
||||
for (const auto &v : values) {
|
||||
auto maybe_value = memgraph::glue::ToBoltValue(v, *storage_, memgraph::storage::View::NEW);
|
||||
auto maybe_value = memgraph::glue::ToBoltValue(v, *interpreter_context_->db, memgraph::storage::View::NEW);
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -95,13 +95,33 @@ std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::De
|
||||
}
|
||||
return decoded_values;
|
||||
}
|
||||
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {}
|
||||
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::query::InterpreterContext *interpreterContext)
|
||||
: interpreter_context_(interpreterContext) {}
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
inline static void MultiDatabaseAuth(const std::optional<auth::User> &user, std::string_view db) {
|
||||
if (user && !AuthChecker::IsUserAuthorized(*user, {}, std::string(db))) {
|
||||
|
||||
void SessionHL::UpdateAndDefunct(const std::string &db_name) {
|
||||
UpdateAndDefunct(ContextWrapper(sc_handler_.Get(db_name)));
|
||||
}
|
||||
void SessionHL::UpdateAndDefunct(ContextWrapper &&cntxt) {
|
||||
defunct_.emplace(std::move(current_));
|
||||
Update(std::forward<ContextWrapper>(cntxt));
|
||||
defunct_->Defunct();
|
||||
}
|
||||
void SessionHL::Update(const std::string &db_name) {
|
||||
ContextWrapper tmp(sc_handler_.Get(db_name));
|
||||
Update(std::move(tmp));
|
||||
}
|
||||
void SessionHL::Update(ContextWrapper &&cntxt) {
|
||||
current_ = std::move(cntxt);
|
||||
interpreter_ = current_.interp();
|
||||
interpreter_->in_explicit_db_ = in_explicit_db_;
|
||||
interpreter_context_ = current_.interpreter_context();
|
||||
}
|
||||
void SessionHL::MultiDatabaseAuth(const std::string &db) {
|
||||
if (user_ && !AuthChecker::IsUserAuthorized(*user_, {}, db)) {
|
||||
throw memgraph::communication::bolt::ClientError(
|
||||
"You are not authorized on the database \"{}\"! Please contact your database administrator.", db);
|
||||
}
|
||||
@@ -112,48 +132,53 @@ std::string SessionHL::GetDefaultDB() {
|
||||
}
|
||||
return memgraph::dbms::kDefaultDB;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string SessionHL::GetCurrentDB() const {
|
||||
if (!interpreter_.current_db_.db_acc_) return "";
|
||||
const auto *db = interpreter_.current_db_.db_acc_->get();
|
||||
return db->id();
|
||||
bool SessionHL::OnDelete(const std::string &db_name) {
|
||||
MG_ASSERT(current_.interpreter_context()->db->id() != db_name && (!defunct_ || defunct_->defunct()),
|
||||
"Trying to delete a database while still in use.");
|
||||
return true;
|
||||
}
|
||||
memgraph::dbms::SetForResult SessionHL::OnChange(const std::string &db_name) {
|
||||
MultiDatabaseAuth(db_name);
|
||||
if (db_name != current_.interpreter_context()->db->id()) {
|
||||
UpdateAndDefunct(db_name); // Done during Pull, so we cannot just replace the current db
|
||||
return memgraph::dbms::SetForResult::SUCCESS;
|
||||
}
|
||||
return memgraph::dbms::SetForResult::ALREADY_SET;
|
||||
}
|
||||
|
||||
#endif
|
||||
std::string SessionHL::GetDatabaseName() const { return interpreter_context_->db->id(); }
|
||||
|
||||
std::optional<std::string> SessionHL::GetServerNameForInit() {
|
||||
auto locked_name = flags::run_time::bolt_server_name_.Lock();
|
||||
return locked_name->empty() ? std::nullopt : std::make_optional(*locked_name);
|
||||
if (FLAGS_bolt_server_name_for_init.empty()) return std::nullopt;
|
||||
return FLAGS_bolt_server_name_for_init;
|
||||
}
|
||||
|
||||
bool SessionHL::Authenticate(const std::string &username, const std::string &password) {
|
||||
bool res = true;
|
||||
interpreter_.ResetUser();
|
||||
{
|
||||
auto locked_auth = auth_->Lock();
|
||||
if (locked_auth->HasUsers()) {
|
||||
user_ = locked_auth->Authenticate(username, password);
|
||||
if (user_.has_value()) {
|
||||
interpreter_.SetUser(user_->username());
|
||||
} else {
|
||||
res = false;
|
||||
}
|
||||
auto locked_auth = auth_->Lock();
|
||||
if (!locked_auth->HasUsers()) {
|
||||
return true;
|
||||
}
|
||||
user_ = locked_auth->Authenticate(username, password);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (user_.has_value()) {
|
||||
const auto &db = user_->db_access().GetDefault();
|
||||
// Check if the underlying database needs to be updated
|
||||
if (db != current_.interpreter_context()->db->id()) {
|
||||
const auto &res = sc_handler_.SetFor(UUID(), db);
|
||||
return res == memgraph::dbms::SetForResult::SUCCESS || res == memgraph::dbms::SetForResult::ALREADY_SET;
|
||||
}
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
// Start off with the default database
|
||||
interpreter_.SetCurrentDB(GetDefaultDB(), false);
|
||||
#endif
|
||||
implicit_db_.emplace(GetCurrentDB());
|
||||
return res;
|
||||
return user_.has_value();
|
||||
}
|
||||
|
||||
void SessionHL::Abort() { interpreter_.Abort(); }
|
||||
void SessionHL::Abort() { interpreter_->Abort(); }
|
||||
|
||||
std::map<std::string, memgraph::communication::bolt::Value> SessionHL::Discard(std::optional<int> n,
|
||||
std::optional<int> qid) {
|
||||
try {
|
||||
memgraph::query::DiscardValueResultStream stream;
|
||||
return DecodeSummary(interpreter_.Pull(&stream, n, qid));
|
||||
return DecodeSummary(interpreter_->Pull(&stream, n, qid));
|
||||
} catch (const memgraph::query::QueryException &e) {
|
||||
// Wrap QueryException into ClientError, because we want to allow the
|
||||
// client to fix their query.
|
||||
@@ -163,18 +188,15 @@ std::map<std::string, memgraph::communication::bolt::Value> SessionHL::Discard(s
|
||||
std::map<std::string, memgraph::communication::bolt::Value> SessionHL::Pull(SessionHL::TEncoder *encoder,
|
||||
std::optional<int> n,
|
||||
std::optional<int> qid) {
|
||||
// TODO: Update once interpreter can handle non-database queries (db_acc will be nullopt)
|
||||
auto *db = interpreter_.current_db_.db_acc_->get();
|
||||
try {
|
||||
TypedValueResultStream<TEncoder> stream(encoder, db->storage());
|
||||
return DecodeSummary(interpreter_.Pull(&stream, n, qid));
|
||||
TypedValueResultStream<TEncoder> stream(encoder, interpreter_context_);
|
||||
return DecodeSummary(interpreter_->Pull(&stream, n, qid));
|
||||
} catch (const memgraph::query::QueryException &e) {
|
||||
// Wrap QueryException into ClientError, because we want to allow the
|
||||
// client to fix their query.
|
||||
throw memgraph::communication::bolt::ClientError(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
const std::string &query, const std::map<std::string, memgraph::communication::bolt::Value> ¶ms,
|
||||
const std::map<std::string, memgraph::communication::bolt::Value> &extra) {
|
||||
@@ -188,18 +210,16 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// TODO: Update once interpreter can handle non-database queries (db_acc will be nullopt)
|
||||
auto *db = interpreter_.current_db_.db_acc_->get();
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
||||
memgraph::storage::PropertyValue(params_pv), db->id());
|
||||
memgraph::storage::PropertyValue(params_pv), interpreter_context_->db->id());
|
||||
}
|
||||
#endif
|
||||
try {
|
||||
auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra));
|
||||
auto result = interpreter_->Prepare(query, params_pv, username, ToQueryExtras(extra), UUID());
|
||||
const std::string db_name = result.db ? *result.db : "";
|
||||
if (user_ && !AuthChecker::IsUserAuthorized(*user_, result.privileges, db_name)) {
|
||||
interpreter_.Abort();
|
||||
interpreter_->Abort();
|
||||
if (db_name.empty()) {
|
||||
throw memgraph::communication::bolt::ClientError(
|
||||
"You are not authorized to execute this query! Please contact your database administrator.");
|
||||
@@ -219,10 +239,10 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
throw memgraph::communication::bolt::ClientError(e.what());
|
||||
}
|
||||
}
|
||||
void SessionHL::RollbackTransaction() { interpreter_.RollbackTransaction(); }
|
||||
void SessionHL::CommitTransaction() { interpreter_.CommitTransaction(); }
|
||||
void SessionHL::RollbackTransaction() { interpreter_->RollbackTransaction(); }
|
||||
void SessionHL::CommitTransaction() { interpreter_->CommitTransaction(); }
|
||||
void SessionHL::BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &extra) {
|
||||
interpreter_.BeginTransaction(ToQueryExtras(extra));
|
||||
interpreter_->BeginTransaction(ToQueryExtras(extra));
|
||||
}
|
||||
void SessionHL::Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -235,68 +255,107 @@ void SessionHL::Configure(const std::map<std::string, memgraph::communication::b
|
||||
throw memgraph::communication::bolt::ClientError("Malformed database name.");
|
||||
}
|
||||
db = db_info.ValueString();
|
||||
const auto ¤t = GetCurrentDB();
|
||||
update = db != current;
|
||||
if (!in_explicit_db_) implicit_db_.emplace(current); // Still not in an explicit database, save for recovery
|
||||
update = db != current_.interpreter_context()->db->id();
|
||||
in_explicit_db_ = true;
|
||||
// NOTE: Once in a transaction, the drivers stop explicitly sending the db and count on using it until commit
|
||||
} else if (in_explicit_db_ && !interpreter_.in_explicit_transaction_) { // Just on a switch
|
||||
if (implicit_db_) {
|
||||
db = *implicit_db_;
|
||||
} else {
|
||||
db = GetDefaultDB();
|
||||
}
|
||||
update = db != GetCurrentDB();
|
||||
} else if (in_explicit_db_ && !interpreter_->in_explicit_transaction_) { // Just on a switch
|
||||
db = GetDefaultDB();
|
||||
update = db != current_.interpreter_context()->db->id();
|
||||
in_explicit_db_ = false;
|
||||
}
|
||||
|
||||
// Check if the underlying database needs to be updated
|
||||
if (update) {
|
||||
MultiDatabaseAuth(user_, db);
|
||||
interpreter_.SetCurrentDB(db, in_explicit_db_);
|
||||
sc_handler_.SetInPlace(db, [this](auto new_sc) mutable {
|
||||
const auto &db_name = new_sc.interpreter_context->db->id();
|
||||
MultiDatabaseAuth(db_name);
|
||||
try {
|
||||
Update(ContextWrapper(new_sc));
|
||||
return memgraph::dbms::SetForResult::SUCCESS;
|
||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||
throw memgraph::communication::bolt::ClientError("No database named \"{}\" found!", db_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
|
||||
const memgraph::communication::v2::ServerEndpoint &endpoint,
|
||||
memgraph::communication::v2::InputStream *input_stream,
|
||||
memgraph::communication::v2::OutputStream *output_stream,
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth
|
||||
SessionHL::~SessionHL() { memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveBoltSessions); }
|
||||
SessionHL::SessionHL(
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
memgraph::audit::Log *audit_log
|
||||
memgraph::dbms::SessionContextHandler &sc_handler,
|
||||
#else
|
||||
memgraph::dbms::SessionContext sc,
|
||||
#endif
|
||||
)
|
||||
const memgraph::communication::v2::ServerEndpoint &endpoint, memgraph::communication::v2::InputStream *input_stream,
|
||||
memgraph::communication::v2::OutputStream *output_stream, const std::string &default_db) // NOLINT
|
||||
: Session<memgraph::communication::v2::InputStream, memgraph::communication::v2::OutputStream>(input_stream,
|
||||
output_stream),
|
||||
interpreter_context_(interpreter_context),
|
||||
interpreter_(interpreter_context_),
|
||||
#ifdef MG_ENTERPRISE
|
||||
audit_log_(audit_log),
|
||||
sc_handler_(sc_handler),
|
||||
current_(sc_handler_.Get(default_db)),
|
||||
#else
|
||||
current_(sc),
|
||||
#endif
|
||||
interpreter_context_(current_.interpreter_context()),
|
||||
interpreter_(current_.interp()),
|
||||
auth_(current_.auth()),
|
||||
#ifdef MG_ENTERPRISE
|
||||
audit_log_(current_.audit_log()),
|
||||
#endif
|
||||
auth_(auth),
|
||||
endpoint_(endpoint),
|
||||
implicit_db_(dbms::kDefaultDB) {
|
||||
// Metrics update
|
||||
run_id_(current_.run_id()) {
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
|
||||
#ifdef MG_ENTERPRISE
|
||||
interpreter_.OnChangeCB([&](std::string_view db_name) { MultiDatabaseAuth(user_, db_name); });
|
||||
#endif
|
||||
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
|
||||
}
|
||||
|
||||
SessionHL::~SessionHL() {
|
||||
memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveBoltSessions);
|
||||
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.erase(&interpreter_); });
|
||||
/// ContextWrapper
|
||||
ContextWrapper::ContextWrapper(memgraph::dbms::SessionContext sc)
|
||||
: session_context(sc),
|
||||
interpreter(std::make_unique<memgraph::query::Interpreter>(session_context.interpreter_context.get())),
|
||||
defunct_(false) {
|
||||
session_context.interpreter_context->interpreters.WithLock(
|
||||
[this](auto &interpreters) { interpreters.insert(interpreter.get()); });
|
||||
}
|
||||
ContextWrapper::~ContextWrapper() { Defunct(); }
|
||||
void ContextWrapper::Defunct() {
|
||||
if (!defunct_) {
|
||||
session_context.interpreter_context->interpreters.WithLock(
|
||||
[this](auto &interpreters) { interpreters.erase(interpreter.get()); });
|
||||
defunct_ = true;
|
||||
}
|
||||
}
|
||||
ContextWrapper::ContextWrapper(ContextWrapper &&in) noexcept
|
||||
: session_context(std::move(in.session_context)), interpreter(std::move(in.interpreter)), defunct_(in.defunct_) {
|
||||
in.defunct_ = true;
|
||||
}
|
||||
ContextWrapper &ContextWrapper::operator=(ContextWrapper &&in) noexcept {
|
||||
if (this != &in) {
|
||||
Defunct();
|
||||
session_context = std::move(in.session_context);
|
||||
interpreter = std::move(in.interpreter);
|
||||
defunct_ = in.defunct_;
|
||||
in.defunct_ = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
memgraph::query::InterpreterContext *ContextWrapper::interpreter_context() {
|
||||
return session_context.interpreter_context.get();
|
||||
}
|
||||
memgraph::query::Interpreter *ContextWrapper::interp() { return interpreter.get(); }
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *ContextWrapper::auth()
|
||||
const {
|
||||
return session_context.auth;
|
||||
}
|
||||
std::string ContextWrapper::run_id() const { return session_context.run_id; }
|
||||
bool ContextWrapper::defunct() const { return defunct_; }
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::audit::Log *ContextWrapper::audit_log() const { return session_context.audit_log; }
|
||||
#endif
|
||||
|
||||
std::map<std::string, memgraph::communication::bolt::Value> SessionHL::DecodeSummary(
|
||||
const std::map<std::string, memgraph::query::TypedValue> &summary) {
|
||||
// TODO: Update once interpreter can handle non-database queries (db_acc will be nullopt)
|
||||
auto *db = interpreter_.current_db_.db_acc_->get();
|
||||
std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
|
||||
for (const auto &kv : summary) {
|
||||
auto maybe_value = ToBoltValue(kv.second, *db->storage(), memgraph::storage::View::NEW);
|
||||
auto maybe_value = ToBoltValue(kv.second, *interpreter_context_->db, memgraph::storage::View::NEW);
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
@@ -313,7 +372,14 @@ std::map<std::string, memgraph::communication::bolt::Value> SessionHL::DecodeSum
|
||||
// This is sent with every query, instead of only on bolt init inside
|
||||
// communication/bolt/v1/states/init.hpp because neo4jdriver does not
|
||||
// read the init message.
|
||||
decoded_summary.emplace("run_id", memgraph::glue::run_id_);
|
||||
if (auto run_id = run_id_; run_id) {
|
||||
decoded_summary.emplace("run_id", *run_id);
|
||||
}
|
||||
|
||||
// Clean up previous session (session gets defunct when switching between databases)
|
||||
if (defunct_) {
|
||||
defunct_.reset();
|
||||
}
|
||||
|
||||
return decoded_summary;
|
||||
}
|
||||
|
||||
@@ -10,28 +10,56 @@
|
||||
// licenses/APL.txt.
|
||||
#pragma once
|
||||
|
||||
#include "audit/log.hpp"
|
||||
#include "auth/auth.hpp"
|
||||
#include "communication/v2/server.hpp"
|
||||
#include "communication/v2/session.hpp"
|
||||
#include "dbms/database.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "dbms/session_context.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "dbms/session_context_handler.hpp"
|
||||
#else
|
||||
#include "dbms/session_context.hpp"
|
||||
#endif
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
struct ContextWrapper {
|
||||
explicit ContextWrapper(memgraph::dbms::SessionContext sc);
|
||||
~ContextWrapper();
|
||||
|
||||
ContextWrapper(const ContextWrapper &) = delete;
|
||||
ContextWrapper &operator=(const ContextWrapper &) = delete;
|
||||
|
||||
ContextWrapper(ContextWrapper &&in) noexcept;
|
||||
ContextWrapper &operator=(ContextWrapper &&in) noexcept;
|
||||
|
||||
void Defunct();
|
||||
memgraph::query::InterpreterContext *interpreter_context();
|
||||
memgraph::query::Interpreter *interp();
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth() const;
|
||||
std::string run_id() const;
|
||||
bool defunct() const;
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::audit::Log *audit_log() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
memgraph::dbms::SessionContext session_context;
|
||||
std::unique_ptr<memgraph::query::Interpreter> interpreter;
|
||||
bool defunct_;
|
||||
};
|
||||
|
||||
class SessionHL final : public memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
|
||||
memgraph::communication::v2::OutputStream> {
|
||||
public:
|
||||
SessionHL(memgraph::query::InterpreterContext *interpreter_context,
|
||||
const memgraph::communication::v2::ServerEndpoint &endpoint,
|
||||
memgraph::communication::v2::InputStream *input_stream,
|
||||
memgraph::communication::v2::OutputStream *output_stream,
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth
|
||||
SessionHL(
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
memgraph::audit::Log *audit_log
|
||||
memgraph::dbms::SessionContextHandler &sc_handler,
|
||||
#else
|
||||
memgraph::dbms::SessionContext sc,
|
||||
#endif
|
||||
);
|
||||
const memgraph::communication::v2::ServerEndpoint &endpoint,
|
||||
memgraph::communication::v2::InputStream *input_stream, memgraph::communication::v2::OutputStream *output_stream,
|
||||
const std::string &default_db = memgraph::dbms::kDefaultDB);
|
||||
|
||||
~SessionHL() override;
|
||||
|
||||
@@ -64,33 +92,70 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
|
||||
void Abort() override;
|
||||
|
||||
// Called during Init
|
||||
// During Init, the user cannot choose the landing DB (switch is done during query execution)
|
||||
bool Authenticate(const std::string &username, const std::string &password) override;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::dbms::SetForResult OnChange(const std::string &db_name) override;
|
||||
|
||||
bool OnDelete(const std::string &db_name) override;
|
||||
#endif
|
||||
std::optional<std::string> GetServerNameForInit() override;
|
||||
|
||||
std::string GetCurrentDB() const override;
|
||||
std::string GetDatabaseName() const override;
|
||||
|
||||
private:
|
||||
std::map<std::string, memgraph::communication::bolt::Value> DecodeSummary(
|
||||
const std::map<std::string, memgraph::query::TypedValue> &summary);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
* @brief Update setup to the new database.
|
||||
*
|
||||
* @param db_name name of the target database
|
||||
* @throws UnknownDatabaseException if handler cannot get it
|
||||
*/
|
||||
void UpdateAndDefunct(const std::string &db_name);
|
||||
|
||||
void UpdateAndDefunct(ContextWrapper &&cntxt);
|
||||
|
||||
void Update(const std::string &db_name);
|
||||
|
||||
void Update(ContextWrapper &&cntxt);
|
||||
|
||||
/**
|
||||
* @brief Authenticate user on passed database.
|
||||
*
|
||||
* @param db database to check against
|
||||
* @throws bolt::ClientError when user is not authorized
|
||||
*/
|
||||
void MultiDatabaseAuth(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Get the user's default database
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string GetDefaultDB();
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::dbms::SessionContextHandler &sc_handler_;
|
||||
#endif
|
||||
ContextWrapper current_;
|
||||
std::optional<ContextWrapper> defunct_;
|
||||
|
||||
memgraph::query::InterpreterContext *interpreter_context_;
|
||||
memgraph::query::Interpreter interpreter_;
|
||||
memgraph::query::Interpreter *interpreter_;
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
||||
std::optional<memgraph::auth::User> user_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::audit::Log *audit_log_;
|
||||
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata
|
||||
#endif
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
||||
memgraph::communication::v2::ServerEndpoint endpoint_;
|
||||
std::optional<std::string> implicit_db_;
|
||||
// NOTE: run_id should be const but that complicates code a lot.
|
||||
std::optional<std::string> run_id_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::glue
|
||||
|
||||
@@ -94,24 +94,20 @@ std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGra
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
auto user = user_.Lock();
|
||||
if (username != user->username()) {
|
||||
auto maybe_user = auth_->ReadLock()->GetUser(username);
|
||||
auto locked_auth = auth_->Lock();
|
||||
if (username != user_.username()) {
|
||||
auto maybe_user = locked_auth->GetUser(username);
|
||||
if (!maybe_user) {
|
||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
}
|
||||
*user = std::move(*maybe_user);
|
||||
user_ = std::move(*maybe_user);
|
||||
}
|
||||
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(*user, dba);
|
||||
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(user_, dba);
|
||||
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void AuthChecker::ClearCache() const {
|
||||
user_.WithLock([](auto &user) mutable { user = {}; });
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
@@ -33,8 +32,6 @@ class AuthChecker : public query::AuthChecker {
|
||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
||||
const std::string &username, const memgraph::query::DbAccessor *dba) const override;
|
||||
|
||||
void ClearCache() const override;
|
||||
|
||||
#endif
|
||||
[[nodiscard]] static bool IsUserAuthorized(const memgraph::auth::User &user,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
@@ -42,7 +39,7 @@ class AuthChecker : public query::AuthChecker {
|
||||
|
||||
private:
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
||||
mutable memgraph::utils::Synchronized<auth::User, memgraph::utils::SpinLock> user_; // cached user
|
||||
mutable auth::User user_;
|
||||
};
|
||||
#ifdef MG_ENTERPRISE
|
||||
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::glue {
|
||||
inline constexpr std::string_view kDefaultUserRoleRegex = "[a-zA-Z0-9_.+-@]+";
|
||||
} // namespace memgraph::glue
|
||||
@@ -406,14 +406,6 @@ bool AuthQueryHandler::SetMainDatabase(const std::string &db, const std::string
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void AuthQueryHandler::DeleteDatabase(std::string_view db) {
|
||||
try {
|
||||
auth_->Lock()->DeleteDatabase(std::string(db));
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AuthQueryHandler::DropRole(const std::string &rolename) {
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
#include <regex>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "auth_global.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/auth_query_handler.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
inline constexpr std::string_view kDefaultUserRoleRegex = "[a-zA-Z0-9_.+-@]+";
|
||||
|
||||
class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
||||
std::string name_regex_string_;
|
||||
@@ -45,8 +46,6 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) override;
|
||||
|
||||
bool SetMainDatabase(const std::string &db, const std::string &username) override;
|
||||
|
||||
void DeleteDatabase(std::string_view db) override;
|
||||
#endif
|
||||
|
||||
bool CreateRole(const std::string &rolename) override;
|
||||
|
||||
@@ -151,8 +151,8 @@ storage::Result<communication::bolt::Vertex> ToBoltVertex(const storage::VertexA
|
||||
properties[db.PropertyToName(prop.first)] = ToBoltValue(prop.second);
|
||||
}
|
||||
// Introduced in Bolt v5 (for now just send the ID)
|
||||
auto element_id = std::to_string(id.AsInt());
|
||||
return communication::bolt::Vertex{id, std::move(labels), std::move(properties), std::move(element_id)};
|
||||
const auto element_id = std::to_string(id.AsInt());
|
||||
return communication::bolt::Vertex{id, labels, properties, element_id};
|
||||
}
|
||||
|
||||
storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccessor &edge, const storage::Storage &db,
|
||||
@@ -171,8 +171,7 @@ storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccesso
|
||||
const auto element_id = std::to_string(id.AsInt());
|
||||
const auto from_element_id = std::to_string(from.AsInt());
|
||||
const auto to_element_id = std::to_string(to.AsInt());
|
||||
return communication::bolt::Edge{
|
||||
id, from, to, std::move(type), std::move(properties), element_id, from_element_id, to_element_id};
|
||||
return communication::bolt::Edge{id, from, to, type, properties, element_id, from_element_id, to_element_id};
|
||||
}
|
||||
|
||||
storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, const storage::Storage &db,
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// 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 "glue/run_id.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
const std::string memgraph::glue::run_id_ = memgraph::utils::GenerateUUID();
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::glue {
|
||||
extern const std::string run_id_;
|
||||
} // namespace memgraph::glue
|
||||
@@ -47,9 +47,10 @@ struct MetricsResponse {
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_histograms{};
|
||||
};
|
||||
|
||||
template <typename TSessionContext>
|
||||
class MetricsService {
|
||||
public:
|
||||
explicit MetricsService(storage::Storage *storage) : db_(storage) {}
|
||||
explicit MetricsService(TSessionContext *session_context) : db_(session_context->interpreter_context->db.get()) {}
|
||||
|
||||
nlohmann::json GetMetricsJSON() {
|
||||
auto response = GetMetrics();
|
||||
@@ -97,7 +98,7 @@ class MetricsService {
|
||||
return metrics_response;
|
||||
}
|
||||
|
||||
inline static std::vector<std::tuple<std::string, std::string, uint64_t>> GetEventCounters() {
|
||||
auto GetEventCounters() {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_counters{};
|
||||
event_counters.reserve(memgraph::metrics::CounterEnd());
|
||||
@@ -110,7 +111,7 @@ class MetricsService {
|
||||
return event_counters;
|
||||
}
|
||||
|
||||
inline static std::vector<std::tuple<std::string, std::string, uint64_t>> GetEventGauges() {
|
||||
auto GetEventGauges() {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_gauges{};
|
||||
event_gauges.reserve(memgraph::metrics::GaugeEnd());
|
||||
@@ -123,7 +124,7 @@ class MetricsService {
|
||||
return event_gauges;
|
||||
}
|
||||
|
||||
inline static std::vector<std::tuple<std::string, std::string, uint64_t>> GetEventHistograms() {
|
||||
auto GetEventHistograms() {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_histograms{};
|
||||
|
||||
@@ -142,11 +143,10 @@ class MetricsService {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Should this be inside Database?
|
||||
// Raw pointer could be dangerous
|
||||
template <typename TSessionContext>
|
||||
class MetricsRequestHandler final {
|
||||
public:
|
||||
explicit MetricsRequestHandler(storage::Storage *storage) : service_(storage) {
|
||||
explicit MetricsRequestHandler(TSessionContext *session_context) : service_(session_context) {
|
||||
spdlog::info("Basic request handler started!");
|
||||
}
|
||||
|
||||
@@ -208,6 +208,6 @@ class MetricsRequestHandler final {
|
||||
}
|
||||
|
||||
private:
|
||||
MetricsService service_;
|
||||
MetricsService<TSessionContext> service_;
|
||||
};
|
||||
} // namespace memgraph::http
|
||||
|
||||
181
src/memgraph.cpp
181
src/memgraph.cpp
@@ -9,23 +9,21 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifndef MG_ENTERPRISE
|
||||
#include "dbms/session_context_handler.hpp"
|
||||
#endif
|
||||
|
||||
#include "audit/log.hpp"
|
||||
#include "communication/websocket/auth.hpp"
|
||||
#include "communication/websocket/server.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "flags/all.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "glue/MonitoringServerT.hpp"
|
||||
#include "glue/ServerT.hpp"
|
||||
#include "glue/auth_checker.hpp"
|
||||
#include "glue/auth_handler.hpp"
|
||||
#include "glue/run_id.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "license/license_sender.hpp"
|
||||
#include "memory/memory_control.hpp"
|
||||
#include "query/config.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "query/procedure/callable_alias_mapper.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "query/procedure/py_module.hpp"
|
||||
@@ -37,18 +35,13 @@
|
||||
#include "utils/terminate_handler.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
#include "query/auth_query_handler.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
|
||||
constexpr const char *kMgUser = "MEMGRAPH_USER";
|
||||
constexpr const char *kMgPassword = "MEMGRAPH_PASSWORD";
|
||||
constexpr const char *kMgPassfile = "MEMGRAPH_PASSFILE";
|
||||
|
||||
// TODO: move elsewhere so that we can remove need of interpreter.hpp
|
||||
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbms::DatabaseAccess &db_acc,
|
||||
std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) {
|
||||
memgraph::query::Interpreter interpreter(&ctx, db_acc);
|
||||
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, std::string cypherl_file_path,
|
||||
memgraph::audit::Log *audit_log = nullptr) {
|
||||
memgraph::query::Interpreter interpreter(&ctx);
|
||||
std::ifstream file(cypherl_file_path);
|
||||
|
||||
if (!file.is_open()) {
|
||||
@@ -107,7 +100,6 @@ void InitSignalHandlers(const std::function<void()> &shutdown_fun) {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
memgraph::memory::SetHooks();
|
||||
google::SetUsageMessage("Memgraph database server");
|
||||
gflags::SetVersionString(version_string);
|
||||
|
||||
@@ -199,27 +191,12 @@ int main(int argc, char **argv) {
|
||||
"won't be available.");
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "You are running Memgraph v" << gflags::VersionString() << std::endl;
|
||||
std::cout << "To get started with Memgraph, visit https://memgr.ph/start" << std::endl;
|
||||
|
||||
auto data_directory = std::filesystem::path(FLAGS_data_directory);
|
||||
|
||||
memgraph::utils::EnsureDirOrDie(data_directory);
|
||||
// Verify that the user that started the process is the same user that is
|
||||
// the owner of the storage directory.
|
||||
memgraph::storage::durability::VerifyStorageDirectoryOwnerAndProcessUserOrDie(data_directory);
|
||||
// Create the lock file and open a handle to it. This will crash the
|
||||
// database if it can't open the file for writing or if any other process is
|
||||
// holding the file opened.
|
||||
memgraph::utils::OutputFile lock_file_handle;
|
||||
lock_file_handle.Open(data_directory / ".lock", memgraph::utils::OutputFile::Mode::OVERWRITE_EXISTING);
|
||||
MG_ASSERT(lock_file_handle.AcquireLock(),
|
||||
"Couldn't acquire lock on the storage directory {}"
|
||||
"!\nAnother Memgraph process is currently running with the same "
|
||||
"storage directory, please stop it first before starting this "
|
||||
"process!",
|
||||
data_directory);
|
||||
|
||||
const auto memory_limit = memgraph::flags::GetMemoryLimit();
|
||||
// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
||||
spdlog::info("Memory limit in config is set to {}", memgraph::utils::GetReadableSize(memory_limit));
|
||||
@@ -232,7 +209,6 @@ int main(int argc, char **argv) {
|
||||
// register all runtime settings
|
||||
memgraph::license::RegisterLicenseSettings(memgraph::license::global_license_checker,
|
||||
memgraph::utils::global_settings);
|
||||
memgraph::flags::run_time::Initialize();
|
||||
|
||||
memgraph::license::global_license_checker.CheckEnvLicense();
|
||||
if (!FLAGS_organization_name.empty() && !FLAGS_license_key.empty()) {
|
||||
@@ -292,8 +268,7 @@ int main(int argc, char **argv) {
|
||||
.name_id_mapper_directory = FLAGS_data_directory + "/rocksdb_name_id_mapper",
|
||||
.id_name_mapper_directory = FLAGS_data_directory + "/rocksdb_id_name_mapper",
|
||||
.durability_directory = FLAGS_data_directory + "/rocksdb_durability",
|
||||
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"},
|
||||
.storage_mode = memgraph::flags::ParseStorageMode()};
|
||||
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"}};
|
||||
if (FLAGS_storage_snapshot_interval_sec == 0) {
|
||||
if (FLAGS_storage_wal_enabled) {
|
||||
LOG_FATAL(
|
||||
@@ -316,6 +291,7 @@ int main(int argc, char **argv) {
|
||||
// Default interpreter configuration
|
||||
memgraph::query::InterpreterConfig interp_config{
|
||||
.query = {.allow_load_csv = FLAGS_allow_load_csv},
|
||||
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
|
||||
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
|
||||
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
|
||||
.default_pulsar_service_url = FLAGS_pulsar_service_url,
|
||||
@@ -343,63 +319,60 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
};
|
||||
|
||||
// WIP
|
||||
#ifdef MG_ENTERPRISE
|
||||
// SessionContext handler (multi-tenancy)
|
||||
memgraph::dbms::SessionContextHandler sc_handler(audit_log, {db_config, interp_config, auth_glue},
|
||||
FLAGS_storage_recover_on_startup || FLAGS_data_recovery_on_startup,
|
||||
FLAGS_storage_delete_on_drop);
|
||||
// Just for current support... TODO remove
|
||||
auto session_context = sc_handler.Get(memgraph::dbms::kDefaultDB);
|
||||
#else
|
||||
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> auth_{data_directory /
|
||||
"auth"};
|
||||
std::unique_ptr<memgraph::query::AuthQueryHandler> auth_handler;
|
||||
std::unique_ptr<memgraph::query::AuthChecker> auth_checker;
|
||||
auth_glue(&auth_, auth_handler, auth_checker);
|
||||
auto session_context = memgraph::dbms::Init(db_config, interp_config, &auth_, auth_handler.get(), auth_checker.get());
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::dbms::DbmsHandler new_handler(db_config, &auth_, FLAGS_data_recovery_on_startup,
|
||||
FLAGS_storage_delete_on_drop);
|
||||
auto db_acc = new_handler.Get(memgraph::dbms::kDefaultDB);
|
||||
memgraph::query::InterpreterContext interpreter_context_(interp_config, &new_handler, auth_handler.get(),
|
||||
auth_checker.get());
|
||||
#else
|
||||
memgraph::utils::Gatekeeper<memgraph::dbms::Database> db_gatekeeper{db_config};
|
||||
auto db_acc_opt = db_gatekeeper.access();
|
||||
MG_ASSERT(db_acc_opt, "Failed to access the main database");
|
||||
auto &db_acc = *db_acc_opt;
|
||||
memgraph::query::InterpreterContext interpreter_context_(interp_config, &db_gatekeeper, auth_handler.get(),
|
||||
auth_checker.get());
|
||||
#endif
|
||||
MG_ASSERT(db_acc, "Failed to access the main database");
|
||||
|
||||
auto *auth = session_context.auth;
|
||||
auto &interpreter_context = *session_context.interpreter_context; // TODO remove
|
||||
|
||||
memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(memgraph::flags::ParseQueryModulesDirectory(),
|
||||
FLAGS_data_directory);
|
||||
memgraph::query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();
|
||||
memgraph::query::procedure::gCallableAliasMapper.LoadMapping(FLAGS_query_callable_mappings_path);
|
||||
|
||||
// TODO Make multi-tenant
|
||||
if (!FLAGS_init_file.empty()) {
|
||||
spdlog::info("Running init file...");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file, &audit_log);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_file, &audit_log);
|
||||
} else {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_file);
|
||||
}
|
||||
#else
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
new_handler.RestoreTriggers(&interpreter_context_);
|
||||
new_handler.RestoreStreams(&interpreter_context_);
|
||||
sc_handler.RestoreTriggers();
|
||||
sc_handler.RestoreStreams();
|
||||
#else
|
||||
{
|
||||
// Triggers can execute query procedures, so we need to reload the modules first and then
|
||||
// the triggers
|
||||
auto storage_accessor = db_acc->Access();
|
||||
auto storage_accessor = interpreter_context.db->Access();
|
||||
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
|
||||
db_acc->trigger_store()->RestoreTriggers(&interpreter_context_.ast_cache, &dba, interpreter_context_.config.query,
|
||||
interpreter_context_.auth_checker);
|
||||
interpreter_context.trigger_store.RestoreTriggers(
|
||||
&interpreter_context.ast_cache, &dba, interpreter_context.config.query, interpreter_context.auth_checker);
|
||||
}
|
||||
|
||||
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
||||
db_acc->streams()->RestoreStreams(db_acc, &interpreter_context_);
|
||||
interpreter_context.streams.RestoreStreams();
|
||||
#endif
|
||||
|
||||
ServerContext context;
|
||||
@@ -415,31 +388,29 @@ int main(int argc, char **argv) {
|
||||
auto server_endpoint = memgraph::communication::v2::ServerEndpoint{
|
||||
boost::asio::ip::address::from_string(FLAGS_bolt_address), static_cast<uint16_t>(FLAGS_bolt_port)};
|
||||
#ifdef MG_ENTERPRISE
|
||||
Context session_context{&interpreter_context_, &auth_, &audit_log};
|
||||
memgraph::glue::ServerT server(server_endpoint, &sc_handler, &context, FLAGS_bolt_session_inactivity_timeout,
|
||||
service_name, FLAGS_bolt_num_workers);
|
||||
#else
|
||||
Context session_context{&interpreter_context_, &auth_};
|
||||
#endif
|
||||
memgraph::glue::ServerT server(server_endpoint, &session_context, &context, FLAGS_bolt_session_inactivity_timeout,
|
||||
service_name, FLAGS_bolt_num_workers);
|
||||
#endif
|
||||
|
||||
const auto machine_id = memgraph::utils::GetMachineId();
|
||||
const auto run_id = session_context.run_id; // For current compatibility
|
||||
|
||||
// Setup telemetry
|
||||
static constexpr auto telemetry_server{"https://telemetry.memgraph.com/88b5e7e8-746a-11e8-9f85-538a9e9690cc/"};
|
||||
std::optional<memgraph::telemetry::Telemetry> telemetry;
|
||||
if (FLAGS_telemetry_enabled) {
|
||||
telemetry.emplace(telemetry_server, data_directory / "telemetry", memgraph::glue::run_id_, machine_id,
|
||||
std::chrono::minutes(10));
|
||||
telemetry.emplace(telemetry_server, data_directory / "telemetry", run_id, machine_id, std::chrono::minutes(10));
|
||||
#ifdef MG_ENTERPRISE
|
||||
telemetry->AddCollector("storage", [&new_handler]() -> nlohmann::json {
|
||||
const auto &info = new_handler.Info();
|
||||
telemetry->AddCollector("storage", [&sc_handler]() -> nlohmann::json {
|
||||
const auto &info = sc_handler.Info();
|
||||
return {{"vertices", info.num_vertex}, {"edges", info.num_edges}, {"databases", info.num_databases}};
|
||||
});
|
||||
#else
|
||||
telemetry->AddCollector("storage", [gk = &db_gatekeeper]() -> nlohmann::json {
|
||||
auto db_acc = gk->access();
|
||||
MG_ASSERT(db_acc, "Failed to get access to the default database");
|
||||
auto info = db_acc->get()->GetInfo();
|
||||
telemetry->AddCollector("storage", [&interpreter_context]() -> nlohmann::json {
|
||||
auto info = interpreter_context.db->GetInfo();
|
||||
return {{"vertices", info.vertex_count}, {"edges", info.edge_count}};
|
||||
});
|
||||
#endif
|
||||
@@ -455,46 +426,67 @@ int main(int argc, char **argv) {
|
||||
return memgraph::query::plan::CallProcedure::GetAndResetCounters();
|
||||
});
|
||||
}
|
||||
memgraph::license::LicenseInfoSender license_info_sender(telemetry_server, memgraph::glue::run_id_, machine_id,
|
||||
memory_limit,
|
||||
memgraph::license::LicenseInfoSender license_info_sender(telemetry_server, run_id, machine_id, memory_limit,
|
||||
memgraph::license::global_license_checker.GetLicenseInfo());
|
||||
|
||||
memgraph::communication::websocket::SafeAuth websocket_auth{&auth_};
|
||||
memgraph::communication::websocket::SafeAuth websocket_auth{auth};
|
||||
memgraph::communication::websocket::Server websocket_server{
|
||||
{FLAGS_monitoring_address, static_cast<uint16_t>(FLAGS_monitoring_port)}, &context, websocket_auth};
|
||||
memgraph::flags::AddLoggerSink(websocket_server.GetLoggingSink());
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// TODO: Make multi-tenant
|
||||
memgraph::glue::MonitoringServerT metrics_server{
|
||||
{FLAGS_metrics_address, static_cast<uint16_t>(FLAGS_metrics_port)}, db_acc->storage(), &context};
|
||||
#endif
|
||||
{FLAGS_metrics_address, static_cast<uint16_t>(FLAGS_metrics_port)}, &session_context, &context};
|
||||
|
||||
// Handler for regular termination signals
|
||||
auto shutdown = [
|
||||
#ifdef MG_ENTERPRISE
|
||||
&metrics_server,
|
||||
#endif
|
||||
&websocket_server, &server, &interpreter_context_] {
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
// Handler for regular termination signals
|
||||
auto shutdown = [&metrics_server, &websocket_server, &server, &sc_handler] {
|
||||
// Server needs to be shutdown first and then the database. This prevents
|
||||
// a race condition when a transaction is accepted during server shutdown.
|
||||
server.Shutdown();
|
||||
// After the server is notified to stop accepting and processing
|
||||
// connections we tell the execution engine to stop processing all pending
|
||||
// queries.
|
||||
sc_handler.Shutdown();
|
||||
|
||||
websocket_server.Shutdown();
|
||||
metrics_server.Shutdown();
|
||||
};
|
||||
|
||||
InitSignalHandlers(shutdown);
|
||||
} else {
|
||||
// Handler for regular termination signals
|
||||
auto shutdown = [&websocket_server, &server, &interpreter_context] {
|
||||
// Server needs to be shutdown first and then the database. This prevents
|
||||
// a race condition when a transaction is accepted during server shutdown.
|
||||
server.Shutdown();
|
||||
// After the server is notified to stop accepting and processing
|
||||
// connections we tell the execution engine to stop processing all pending
|
||||
// queries.
|
||||
memgraph::query::Shutdown(&interpreter_context);
|
||||
|
||||
websocket_server.Shutdown();
|
||||
};
|
||||
|
||||
InitSignalHandlers(shutdown);
|
||||
}
|
||||
#else
|
||||
// Handler for regular termination signals
|
||||
auto shutdown = [&websocket_server, &server, &interpreter_context] {
|
||||
// Server needs to be shutdown first and then the database. This prevents
|
||||
// a race condition when a transaction is accepted during server shutdown.
|
||||
server.Shutdown();
|
||||
// After the server is notified to stop accepting and processing
|
||||
// connections we tell the execution engine to stop processing all pending
|
||||
// queries.
|
||||
interpreter_context_.Shutdown();
|
||||
memgraph::query::Shutdown(&interpreter_context);
|
||||
|
||||
websocket_server.Shutdown();
|
||||
#ifdef MG_ENTERPRISE
|
||||
metrics_server.Shutdown();
|
||||
#endif
|
||||
};
|
||||
|
||||
InitSignalHandlers(shutdown);
|
||||
#endif
|
||||
|
||||
// Release the temporary database access
|
||||
db_acc.reset();
|
||||
|
||||
// Startup the main server
|
||||
MG_ASSERT(server.Start(), "Couldn't start the Bolt server!");
|
||||
websocket_server.Start();
|
||||
|
||||
@@ -507,16 +499,13 @@ int main(int argc, char **argv) {
|
||||
if (!FLAGS_init_data_file.empty()) {
|
||||
spdlog::info("Running init data file.");
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto db_acc = new_handler.Get(memgraph::dbms::kDefaultDB);
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_data_file, &audit_log);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_data_file, &audit_log);
|
||||
} else {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_data_file);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_data_file);
|
||||
}
|
||||
#else
|
||||
auto db_acc_2 = db_gatekeeper.access();
|
||||
MG_ASSERT(db_acc_2, "Failed to gain access to the main database");
|
||||
InitFromCypherlFile(interpreter_context_, *db_acc_2, FLAGS_init_data_file);
|
||||
InitFromCypherlFile(interpreter_context, FLAGS_init_data_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@ set(memory_src_files
|
||||
new_delete.cpp
|
||||
memory_control.cpp)
|
||||
|
||||
|
||||
|
||||
find_package(jemalloc REQUIRED)
|
||||
find_package(Jemalloc REQUIRED)
|
||||
|
||||
add_library(mg-memory STATIC ${memory_src_files})
|
||||
target_link_libraries(mg-memory mg-utils fmt)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -10,11 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "memory_control.hpp"
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
|
||||
#if USE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
@@ -27,274 +22,6 @@ namespace memgraph::memory {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
|
||||
std::string get_string_thread_id(const std::thread::id &thread_id) {
|
||||
std::ostringstream oss;
|
||||
oss << thread_id;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// TODO (af) think if following implementation would make sense
|
||||
/*
|
||||
std::string get_thread_id() {
|
||||
static thread_local std::thread::id current_thread_id = std::this_thread::get_id();
|
||||
// figure out how to cache this part
|
||||
std::ostringstream oss;
|
||||
oss << current_thread_id;
|
||||
return oss.str();
|
||||
}
|
||||
*/
|
||||
std::string get_thread_id() { return get_string_thread_id(std::this_thread::get_id()); }
|
||||
|
||||
#if USE_JEMALLOC
|
||||
|
||||
static void *my_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
bool *commit, unsigned arena_ind);
|
||||
static bool my_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size, bool committed, unsigned arena_ind);
|
||||
static void my_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size, bool committed, unsigned arena_ind);
|
||||
static bool my_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind);
|
||||
static bool my_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind);
|
||||
static bool my_purge_forced(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind);
|
||||
extent_hooks_t *old_hooks = nullptr;
|
||||
|
||||
static extent_hooks_t custom_hooks = {
|
||||
.alloc = &my_alloc,
|
||||
.dalloc = &my_dalloc,
|
||||
.destroy = &my_destroy,
|
||||
.commit = &my_commit,
|
||||
.decommit = &my_decommit,
|
||||
.purge_lazy = nullptr,
|
||||
.purge_forced = &my_purge_forced,
|
||||
.split = nullptr,
|
||||
.merge = nullptr,
|
||||
};
|
||||
|
||||
static const extent_hooks_t *new_hooks = &custom_hooks;
|
||||
|
||||
void *my_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit,
|
||||
unsigned arena_ind) {
|
||||
// This needs to be before, to throw exception in case of too big alloc
|
||||
if (*commit) [[likely]] {
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Alloc(static_cast<int64_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
auto *ptr = old_hooks->alloc(extent_hooks, new_addr, size, alignment, zero, commit, arena_ind);
|
||||
if (ptr == nullptr) [[unlikely]] {
|
||||
if (*commit) {
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(size));
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Free(static_cast<int64_t>(size));
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static bool my_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size, bool committed, unsigned arena_ind) {
|
||||
auto err = old_hooks->dalloc(extent_hooks, addr, size, committed, arena_ind);
|
||||
|
||||
if (err) [[unlikely]] {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (committed) [[likely]] {
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(size));
|
||||
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Free(static_cast<int64_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void my_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size, bool committed, unsigned arena_ind) {
|
||||
if (committed) [[likely]] {
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(size));
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Free(static_cast<int64_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
old_hooks->destroy(extent_hooks, addr, size, committed, arena_ind);
|
||||
}
|
||||
|
||||
static bool my_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind) {
|
||||
auto err = old_hooks->commit(extent_hooks, addr, size, offset, length, arena_ind);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(length));
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Alloc(static_cast<int64_t>(size));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool my_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind) {
|
||||
MG_ASSERT(old_hooks && old_hooks->decommit);
|
||||
auto err = old_hooks->decommit(extent_hooks, addr, size, offset, length, arena_ind);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(length));
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Free(static_cast<int64_t>(size));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool my_purge_forced(extent_hooks_t *extent_hooks, void *addr, size_t size, size_t offset, size_t length,
|
||||
unsigned arena_ind) {
|
||||
MG_ASSERT(old_hooks && old_hooks->purge_forced);
|
||||
auto err = old_hooks->purge_forced(extent_hooks, addr, size, offset, length, arena_ind);
|
||||
|
||||
if (err) [[unlikely]] {
|
||||
return err;
|
||||
}
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(length));
|
||||
|
||||
if (arena_tracking[arena_ind]) [[unlikely]] {
|
||||
transaction_id_tracker[thread_id_to_transaction_id[get_thread_id()]].Free(static_cast<int64_t>(size));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void SetHooks() {
|
||||
#if USE_JEMALLOC
|
||||
|
||||
uint64_t allocated{0};
|
||||
uint64_t sz{sizeof(allocated)};
|
||||
|
||||
sz = sizeof(unsigned);
|
||||
unsigned n_arenas{0};
|
||||
int err = mallctl("opt.narenas", (void *)&n_arenas, &sz, nullptr, 0);
|
||||
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::trace("n areanas {}", n_arenas);
|
||||
|
||||
if (nullptr != old_hooks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_arenas; i++) {
|
||||
arena_tracking[i] = 0;
|
||||
std::string func_name = "arena." + std::to_string(i) + ".extent_hooks";
|
||||
|
||||
size_t hooks_len = sizeof(old_hooks);
|
||||
|
||||
int err = mallctl(func_name.c_str(), &old_hooks, &hooks_len, nullptr, 0);
|
||||
|
||||
if (err) {
|
||||
LOG_FATAL("Error getting hooks for jemalloc arena {}", i);
|
||||
}
|
||||
|
||||
// Due to the way jemalloc works, we need first to set their hooks
|
||||
// which will trigger creating arena, then we can set our custom hook wrappers
|
||||
|
||||
err = mallctl(func_name.c_str(), nullptr, nullptr, &old_hooks, sizeof(old_hooks));
|
||||
|
||||
MG_ASSERT(old_hooks);
|
||||
MG_ASSERT(old_hooks->alloc);
|
||||
MG_ASSERT(old_hooks->dalloc);
|
||||
MG_ASSERT(old_hooks->destroy);
|
||||
MG_ASSERT(old_hooks->commit);
|
||||
MG_ASSERT(old_hooks->decommit);
|
||||
MG_ASSERT(old_hooks->purge_forced);
|
||||
MG_ASSERT(old_hooks->purge_lazy);
|
||||
MG_ASSERT(old_hooks->split);
|
||||
MG_ASSERT(old_hooks->merge);
|
||||
|
||||
custom_hooks.purge_lazy = old_hooks->purge_lazy;
|
||||
custom_hooks.split = old_hooks->split;
|
||||
custom_hooks.merge = old_hooks->merge;
|
||||
|
||||
if (err) {
|
||||
LOG_FATAL("Error setting jemalloc hooks for jemalloc arena {}", i);
|
||||
}
|
||||
|
||||
err = mallctl(func_name.c_str(), nullptr, nullptr, &new_hooks, sizeof(new_hooks));
|
||||
|
||||
if (err) {
|
||||
LOG_FATAL("Error setting custom hooks for jemalloc arena {}", i);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned GetArenaForThread() {
|
||||
#if USE_JEMALLOC
|
||||
unsigned thread_arena{0};
|
||||
size_t size_thread_arena = sizeof(thread_arena);
|
||||
int err = mallctl("thread.arena", &thread_arena, &size_thread_arena, nullptr, 0);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
return thread_arena;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool AddTrackingOnArena(unsigned arena_id) {
|
||||
#if USE_JEMALLOC
|
||||
arena_tracking[arena_id].fetch_add(1);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RemoveTrackingOnArena(unsigned arena_id) {
|
||||
#if USE_JEMALLOC
|
||||
arena_tracking[arena_id].fetch_sub(1);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateThreadToTransactionId(const std::thread::id &thread_id, uint64_t transaction_id) {
|
||||
thread_id_to_transaction_id[get_string_thread_id(thread_id)] = transaction_id;
|
||||
}
|
||||
|
||||
void UpdateThreadToTransactionId(const char *thread_id, uint64_t transaction_id) {
|
||||
thread_id_to_transaction_id[std::string(thread_id)] = transaction_id;
|
||||
}
|
||||
|
||||
void ResetThreadToTransactionId(const std::thread::id &thread_id) {
|
||||
thread_id_to_transaction_id.erase(get_string_thread_id(thread_id));
|
||||
}
|
||||
|
||||
void ResetThreadToTransactionId(const char *thread_id) { thread_id_to_transaction_id.erase(std::string(thread_id)); }
|
||||
|
||||
void AddTrackingsOnCurrentThread(uint64_t transaction_id) {
|
||||
UpdateThreadToTransactionId(std::this_thread::get_id(), transaction_id);
|
||||
AddTrackingOnArena(memgraph::memory::GetArenaForThread());
|
||||
}
|
||||
|
||||
void RemoveTrackingsOnCurrentThread() {
|
||||
ResetThreadToTransactionId(std::this_thread::get_id());
|
||||
RemoveTrackingOnArena(GetArenaForThread());
|
||||
}
|
||||
|
||||
void PurgeUnusedMemory() {
|
||||
#if USE_JEMALLOC
|
||||
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
|
||||
@@ -303,5 +30,4 @@ void PurgeUnusedMemory() {
|
||||
|
||||
#undef STRINGIFY
|
||||
#undef STRINGIFY_HELPER
|
||||
|
||||
} // namespace memgraph::memory
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -11,34 +11,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
|
||||
#include <thread>
|
||||
namespace memgraph::memory {
|
||||
|
||||
void PurgeUnusedMemory();
|
||||
void SetHooks();
|
||||
|
||||
// TODO(af) This should all be part of memgraph::memory::thread namespace, and moved to different file
|
||||
// This should be part of class
|
||||
unsigned GetArenaForThread();
|
||||
bool AddTrackingOnArena(unsigned);
|
||||
bool RemoveTrackingOnArena(unsigned);
|
||||
void UpdateThreadToTransactionId(const std::thread::id &, uint64_t);
|
||||
void ResetThreadToTransactionId(const std::thread::id &);
|
||||
void UpdateThreadToTransactionId(const char *, uint64_t);
|
||||
void ResetThreadToTransactionId(const char *);
|
||||
void AddTrackingsOnCurrentThread(uint64_t);
|
||||
void RemoveTrackingsOnCurrentThread();
|
||||
|
||||
inline std::unordered_map<std::string, uint64_t> thread_id_to_transaction_id;
|
||||
// TODO(af): think if we need to solve issue of tracking allocations for arena
|
||||
// if user forgets to unregister tracking for that thread before it dies.
|
||||
inline std::unordered_map<unsigned, std::atomic<int>> arena_tracking;
|
||||
inline std::unordered_map<uint64_t, utils::MemoryTracker> transaction_id_tracker;
|
||||
|
||||
} // namespace memgraph::memory
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -87,15 +87,21 @@ void deleteSized(void *ptr, const std::size_t /*unused*/, const std::align_val_t
|
||||
#endif
|
||||
|
||||
void TrackMemory(std::size_t size) {
|
||||
#if !USE_JEMALLOC
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
|
||||
#if USE_JEMALLOC
|
||||
if (size != 0) [[likely]] {
|
||||
size = nallocx(size, 0);
|
||||
}
|
||||
#endif
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
|
||||
}
|
||||
|
||||
void TrackMemory(std::size_t size, const std::align_val_t align) {
|
||||
#if !USE_JEMALLOC
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
|
||||
#if USE_JEMALLOC
|
||||
if (size != 0) [[likely]] {
|
||||
size = nallocx(size, MALLOCX_ALIGN(align)); // NOLINT(hicpp-signed-bitwise)
|
||||
}
|
||||
#endif
|
||||
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
|
||||
}
|
||||
|
||||
bool TrackMemoryNoExcept(const std::size_t size) {
|
||||
@@ -120,7 +126,11 @@ bool TrackMemoryNoExcept(const std::size_t size, const std::align_val_t align) {
|
||||
|
||||
void UntrackMemory([[maybe_unused]] void *ptr, [[maybe_unused]] std::size_t size = 0) noexcept {
|
||||
try {
|
||||
#if !USE_JEMALLOC
|
||||
#if USE_JEMALLOC
|
||||
if (ptr != nullptr) [[likely]] {
|
||||
memgraph::utils::total_memory_tracker.Free(sallocx(ptr, 0));
|
||||
}
|
||||
#else
|
||||
if (size) {
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(size));
|
||||
} else {
|
||||
@@ -134,7 +144,11 @@ void UntrackMemory([[maybe_unused]] void *ptr, [[maybe_unused]] std::size_t size
|
||||
|
||||
void UntrackMemory(void *ptr, const std::align_val_t align, [[maybe_unused]] std::size_t size = 0) noexcept {
|
||||
try {
|
||||
#if !USE_JEMALLOC
|
||||
#if USE_JEMALLOC
|
||||
if (ptr != nullptr) [[likely]] {
|
||||
memgraph::utils::total_memory_tracker.Free(sallocx(ptr, MALLOCX_ALIGN(align))); // NOLINT(hicpp-signed-bitwise)
|
||||
}
|
||||
#else
|
||||
if (size) {
|
||||
memgraph::utils::total_memory_tracker.Free(static_cast<int64_t>(size));
|
||||
} else {
|
||||
|
||||
@@ -36,26 +36,11 @@ set(mg_query_sources
|
||||
trigger_context.cpp
|
||||
typed_value.cpp
|
||||
graph.cpp
|
||||
db_accessor.cpp
|
||||
auth_query_handler.cpp
|
||||
interpreter_context.cpp
|
||||
)
|
||||
db_accessor.cpp)
|
||||
|
||||
add_library(mg-query STATIC ${mg_query_sources})
|
||||
target_include_directories(mg-query PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-query PUBLIC dl
|
||||
cppitertools
|
||||
Python3::Python
|
||||
mg-integrations-pulsar
|
||||
mg-integrations-kafka
|
||||
mg-storage-v2
|
||||
mg-license
|
||||
mg-utils
|
||||
mg-kvstore
|
||||
mg-memory
|
||||
mg::csv
|
||||
mg-flags
|
||||
mg-dbms)
|
||||
target_link_libraries(mg-query PUBLIC dl cppitertools Python3::Python mg-integrations-pulsar mg-integrations-kafka mg-storage-v2 mg-license mg-utils mg-kvstore mg-memory mg::csv)
|
||||
if(NOT "${MG_PYTHON_PATH}" STREQUAL "")
|
||||
set(Python3_ROOT_DIR "${MG_PYTHON_PATH}")
|
||||
endif()
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
|
||||
@@ -23,21 +19,17 @@ namespace memgraph::query {
|
||||
|
||||
class FineGrainedAuthChecker;
|
||||
|
||||
class DbAccessor;
|
||||
|
||||
class AuthChecker {
|
||||
public:
|
||||
virtual ~AuthChecker() = default;
|
||||
|
||||
[[nodiscard]] virtual bool IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<AuthQuery::Privilege> &privileges,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
[[nodiscard]] virtual std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
||||
const std::string &username, const DbAccessor *db_accessor) const = 0;
|
||||
|
||||
virtual void ClearCache() const = 0;
|
||||
const std::string &username, const memgraph::query::DbAccessor *db_accessor) const = 0;
|
||||
#endif
|
||||
};
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -45,73 +37,73 @@ class FineGrainedAuthChecker {
|
||||
public:
|
||||
virtual ~FineGrainedAuthChecker() = default;
|
||||
|
||||
[[nodiscard]] virtual bool Has(const VertexAccessor &vertex, memgraph::storage::View view,
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
[[nodiscard]] virtual bool Has(const query::VertexAccessor &vertex, memgraph::storage::View view,
|
||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool Has(const EdgeAccessor &edge,
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
[[nodiscard]] virtual bool Has(const query::EdgeAccessor &edge,
|
||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool Has(const std::vector<memgraph::storage::LabelId> &labels,
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool Has(const memgraph::storage::EdgeTypeId &edge_type,
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool HasGlobalPrivilegeOnVertices(
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool HasGlobalPrivilegeOnEdges(
|
||||
AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||
};
|
||||
|
||||
class AllowEverythingFineGrainedAuthChecker final : public FineGrainedAuthChecker {
|
||||
class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAuthChecker {
|
||||
public:
|
||||
bool Has(const VertexAccessor & /*vertex*/, const memgraph::storage::View /*view*/,
|
||||
const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
const query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Has(const EdgeAccessor & /*edge*/,
|
||||
const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
bool Has(const memgraph::query::EdgeAccessor & /*edge*/,
|
||||
const query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Has(const std::vector<memgraph::storage::LabelId> & /*labels*/,
|
||||
const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
const query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Has(const memgraph::storage::EdgeTypeId & /*edge_type*/,
|
||||
const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
const query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HasGlobalPrivilegeOnVertices(const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
bool HasGlobalPrivilegeOnVertices(
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HasGlobalPrivilegeOnEdges(const AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
bool HasGlobalPrivilegeOnEdges(
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}; // namespace memgraph::query
|
||||
#endif
|
||||
|
||||
class AllowEverythingAuthChecker final : public AuthChecker {
|
||||
class AllowEverythingAuthChecker final : public query::AuthChecker {
|
||||
public:
|
||||
bool IsUserAuthorized(const std::optional<std::string> & /*username*/,
|
||||
const std::vector<AuthQuery::Privilege> & /*privileges*/,
|
||||
const std::vector<query::AuthQuery::Privilege> & /*privileges*/,
|
||||
const std::string & /*db*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(const std::string & /*username*/,
|
||||
const DbAccessor * /*dba*/) const override {
|
||||
const query::DbAccessor * /*dba*/) const override {
|
||||
return std::make_unique<AllowEverythingFineGrainedAuthChecker>();
|
||||
}
|
||||
|
||||
void ClearCache() const override {}
|
||||
#endif
|
||||
};
|
||||
}; // namespace memgraph::query
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// 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 "query/auth_query_handler.hpp"
|
||||
@@ -1,126 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "query/frontend/ast/ast.hpp" // overkill
|
||||
#include "query/typed_value.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
class AuthQueryHandler {
|
||||
public:
|
||||
AuthQueryHandler() = default;
|
||||
virtual ~AuthQueryHandler() = default;
|
||||
|
||||
AuthQueryHandler(const AuthQueryHandler &) = delete;
|
||||
AuthQueryHandler(AuthQueryHandler &&) = delete;
|
||||
AuthQueryHandler &operator=(const AuthQueryHandler &) = delete;
|
||||
AuthQueryHandler &operator=(AuthQueryHandler &&) = delete;
|
||||
|
||||
/// Return false if the user already exists.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool CreateUser(const std::string &username, const std::optional<std::string> &password) = 0;
|
||||
|
||||
/// Return false if the user does not exist.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool DropUser(const std::string &username) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void SetPassword(const std::string &username, const std::optional<std::string> &password) = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/// Return true if access revoked successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool RevokeDatabaseFromUser(const std::string &db, const std::string &username) = 0;
|
||||
|
||||
/// Return true if access granted successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool GrantDatabaseToUser(const std::string &db, const std::string &username) = 0;
|
||||
|
||||
/// Returns database access rights for the user
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) = 0;
|
||||
|
||||
/// Return true if main database set successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool SetMainDatabase(const std::string &db, const std::string &username) = 0;
|
||||
|
||||
/// Delete database from all users
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void DeleteDatabase(std::string_view db) = 0;
|
||||
#endif
|
||||
|
||||
/// Return false if the role already exists.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool CreateRole(const std::string &rolename) = 0;
|
||||
|
||||
/// Return false if the role does not exist.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool DropRole(const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<memgraph::query::TypedValue> GetUsernames() = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<memgraph::query::TypedValue> GetRolenames() = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::optional<std::string> GetRolenameForUser(const std::string &username) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<memgraph::query::TypedValue> GetUsernamesForRole(const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void SetRole(const std::string &username, const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void ClearRole(const std::string &username) = 0;
|
||||
|
||||
virtual std::vector<std::vector<memgraph::query::TypedValue>> GetPrivileges(const std::string &user_or_role) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void GrantPrivilege(
|
||||
const std::string &user_or_role, const std::vector<memgraph::query::AuthQuery::Privilege> &privileges
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
const std::vector<std::unordered_map<memgraph::query::AuthQuery::FineGrainedPrivilege, std::vector<std::string>>>
|
||||
&label_privileges,
|
||||
|
||||
const std::vector<std::unordered_map<memgraph::query::AuthQuery::FineGrainedPrivilege, std::vector<std::string>>>
|
||||
&edge_type_privileges
|
||||
#endif
|
||||
) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void DenyPrivilege(const std::string &user_or_role,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RevokePrivilege(
|
||||
const std::string &user_or_role, const std::vector<memgraph::query::AuthQuery::Privilege> &privileges
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
const std::vector<std::unordered_map<memgraph::query::AuthQuery::FineGrainedPrivilege, std::vector<std::string>>>
|
||||
&label_privileges,
|
||||
|
||||
const std::vector<std::unordered_map<memgraph::query::AuthQuery::FineGrainedPrivilege, std::vector<std::string>>>
|
||||
&edge_type_privileges
|
||||
#endif
|
||||
) = 0;
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -19,6 +19,8 @@ struct InterpreterConfig {
|
||||
bool allow_load_csv{true};
|
||||
} query;
|
||||
|
||||
// The default execution timeout is 10 minutes.
|
||||
double execution_timeout_sec{600.0};
|
||||
// The same as \ref memgraph::storage::replication::ReplicationClientConfig
|
||||
std::chrono::seconds replication_replica_check_frequency{1};
|
||||
|
||||
|
||||
@@ -51,8 +51,6 @@ struct EvaluationContext {
|
||||
/// All counters generated by `counter` function, mutable because the function
|
||||
/// modifies the values
|
||||
mutable std::unordered_map<std::string, int64_t> counters{};
|
||||
/// Property lookup cache ({symbol: {property_id: property_value, ...}, ...})
|
||||
mutable std::unordered_map<int32_t, std::map<storage::PropertyId, storage::PropertyValue>> property_lookups_cache{};
|
||||
};
|
||||
|
||||
inline std::vector<storage::PropertyId> NamesToProperties(const std::vector<std::string> &property_names,
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "query/cypher_query_interpreter.hpp"
|
||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||
#include "query/frontend/opencypher/parser.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(query_cost_planner, true, "Use the cost-estimating query planner.");
|
||||
@@ -78,7 +76,7 @@ ParsedQuery ParseQuery(const std::string &query_string, const std::map<std::stri
|
||||
|
||||
// Convert the ANTLR4 parse tree into an AST.
|
||||
AstStorage ast_storage;
|
||||
frontend::ParsingContext context{.is_query_cached = true};
|
||||
frontend::ParsingContext context{true};
|
||||
frontend::CypherMainVisitor visitor(context, &ast_storage);
|
||||
|
||||
visitor.visit(parser->tree());
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/config.hpp"
|
||||
#include "query/frontend/ast/cypher_main_visitor.hpp"
|
||||
#include "query/frontend/opencypher/parser.hpp"
|
||||
#include "query/frontend/semantic/required_privileges.hpp"
|
||||
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||
#include "query/frontend/stripped.hpp"
|
||||
|
||||
@@ -21,18 +21,6 @@ namespace memgraph::query {
|
||||
SubgraphDbAccessor::SubgraphDbAccessor(query::DbAccessor db_accessor, Graph *graph)
|
||||
: db_accessor_(db_accessor), graph_(graph) {}
|
||||
|
||||
void SubgraphDbAccessor::TrackThreadAllocations(const char *thread_id) {
|
||||
return db_accessor_.TrackThreadAllocations(thread_id);
|
||||
}
|
||||
|
||||
void SubgraphDbAccessor::TrackCurrentThreadAllocations() { return db_accessor_.TrackCurrentThreadAllocations(); }
|
||||
|
||||
void SubgraphDbAccessor::UntrackThreadAllocations(const char *thread_id) {
|
||||
return db_accessor_.UntrackThreadAllocations(thread_id);
|
||||
}
|
||||
|
||||
void SubgraphDbAccessor::UntrackCurrentThreadAllocations() { return db_accessor_.TrackCurrentThreadAllocations(); }
|
||||
|
||||
storage::PropertyId SubgraphDbAccessor::NameToProperty(const std::string_view name) {
|
||||
return db_accessor_.NameToProperty(name);
|
||||
}
|
||||
@@ -88,24 +76,6 @@ SubgraphDbAccessor::DetachRemoveVertex( // NOLINT(readability-convert-member-fu
|
||||
"Vertex holds only partial information about edges. Cannot detach delete safely while using projected graph."};
|
||||
}
|
||||
|
||||
storage::Result<EdgeAccessor> SubgraphDbAccessor::EdgeSetFrom(EdgeAccessor *edge, SubgraphVertexAccessor *new_from) {
|
||||
VertexAccessor *new_from_impl = &new_from->impl_;
|
||||
if (!this->graph_->ContainsVertex(*new_from_impl)) {
|
||||
throw std::logic_error{"Projected graph must contain the new `from` vertex!"};
|
||||
}
|
||||
auto result = db_accessor_.EdgeSetFrom(edge, new_from_impl);
|
||||
return result;
|
||||
}
|
||||
|
||||
storage::Result<EdgeAccessor> SubgraphDbAccessor::EdgeSetTo(EdgeAccessor *edge, SubgraphVertexAccessor *new_to) {
|
||||
VertexAccessor *new_to_impl = &new_to->impl_;
|
||||
if (!this->graph_->ContainsVertex(*new_to_impl)) {
|
||||
throw std::logic_error{"Projected graph must contain the new `to` vertex!"};
|
||||
}
|
||||
auto result = db_accessor_.EdgeSetTo(edge, new_to_impl);
|
||||
return result;
|
||||
}
|
||||
|
||||
storage::Result<std::optional<VertexAccessor>> SubgraphDbAccessor::RemoveVertex(
|
||||
SubgraphVertexAccessor *subgraphvertex_accessor) {
|
||||
VertexAccessor *vertex_accessor = &subgraphvertex_accessor->impl_;
|
||||
@@ -141,10 +111,10 @@ query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
|
||||
|
||||
VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() const { return impl_; }
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::OutEdges(storage::View view) const {
|
||||
auto SubgraphVertexAccessor::OutEdges(storage::View view) const -> decltype(impl_.OutEdges(view)) {
|
||||
auto maybe_edges = impl_.impl_.OutEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(maybe_edges->edges);
|
||||
auto edges = std::move(*maybe_edges);
|
||||
const auto &graph_edges = graph_->edges();
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
@@ -155,18 +125,13 @@ storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::OutEdges(stora
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> resulting_edges;
|
||||
resulting_edges.reserve(filteredOutEdges.size());
|
||||
std::ranges::transform(filteredOutEdges, std::back_inserter(resulting_edges),
|
||||
[](auto const &edge) { return VertexAccessor::MakeEdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = std::move(resulting_edges), .expanded_count = maybe_edges->expanded_count};
|
||||
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
|
||||
}
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::InEdges(storage::View view) const {
|
||||
auto SubgraphVertexAccessor::InEdges(storage::View view) const -> decltype(impl_.InEdges(view)) {
|
||||
auto maybe_edges = impl_.impl_.InEdges(view, {});
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
auto edges = std::move(maybe_edges->edges);
|
||||
auto edges = std::move(*maybe_edges);
|
||||
const auto &graph_edges = graph_->edges();
|
||||
|
||||
std::vector<storage::EdgeAccessor> filteredOutEdges;
|
||||
@@ -177,12 +142,7 @@ storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::InEdges(storag
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> resulting_edges;
|
||||
resulting_edges.reserve(filteredOutEdges.size());
|
||||
std::ranges::transform(filteredOutEdges, std::back_inserter(resulting_edges),
|
||||
[](auto const &edge) { return VertexAccessor::MakeEdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = std::move(resulting_edges), .expanded_count = maybe_edges->expanded_count};
|
||||
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
|
||||
}
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -12,24 +12,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
|
||||
#include <cppitertools/filter.hpp>
|
||||
#include <cppitertools/imap.hpp>
|
||||
|
||||
#include "memory/memory_control.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/result.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "storage/v2/storage_mode.hpp"
|
||||
#include "utils/pmr/unordered_set.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Our communication layer and query engine don't mix
|
||||
// very well on Centos because OpenSSL version available
|
||||
// on Centos 7 include libkrb5 which has brilliant macros
|
||||
// called TRUE and FALSE. For more detailed explanation go
|
||||
// to memgraph.cpp.
|
||||
//
|
||||
// Because of the replication storage now uses some form of
|
||||
// communication so we have some unwanted macros.
|
||||
// This cannot be avoided by simple include orderings so we
|
||||
// simply undefine those macros as we're sure that libkrb5
|
||||
// won't and can't be used anywhere in the query engine.
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
#undef FALSE
|
||||
#undef TRUE
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include "storage/v2/view.hpp"
|
||||
#include "utils/bound.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/pmr/unordered_set.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
@@ -78,14 +94,6 @@ class EdgeAccessor final {
|
||||
|
||||
VertexAccessor From() const;
|
||||
|
||||
/// When edge is deleted and you are accessing To vertex
|
||||
/// for_deleted_ flag will in this case be updated properly
|
||||
VertexAccessor DeletedEdgeToVertex() const;
|
||||
|
||||
/// When edge is deleted and you are accessing From vertex
|
||||
/// for_deleted_ flag will in this case be updated properly
|
||||
VertexAccessor DeletedEdgeFromVertex() const;
|
||||
|
||||
bool IsCycle() const;
|
||||
|
||||
int64_t CypherId() const { return impl_.Gid().AsInt(); }
|
||||
@@ -97,11 +105,6 @@ class EdgeAccessor final {
|
||||
bool operator!=(const EdgeAccessor &e) const noexcept { return !(*this == e); }
|
||||
};
|
||||
|
||||
struct EdgeVertexAccessorResult {
|
||||
std::vector<EdgeAccessor> edges;
|
||||
int64_t expanded_count;
|
||||
};
|
||||
|
||||
class VertexAccessor final {
|
||||
public:
|
||||
storage::VertexAccessor impl_;
|
||||
@@ -150,62 +153,37 @@ class VertexAccessor final {
|
||||
return impl_.ClearProperties();
|
||||
}
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view,
|
||||
const std::vector<storage::EdgeTypeId> &edge_types) const {
|
||||
auto maybe_result = impl_.InEdges(view, edge_types);
|
||||
if (maybe_result.HasError()) return maybe_result.GetError();
|
||||
|
||||
std::vector<EdgeAccessor> edges;
|
||||
edges.reserve((*maybe_result).edges.size());
|
||||
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
|
||||
[](auto const &edge) { return EdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
|
||||
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
|
||||
auto maybe_edges = impl_.InEdges(view, edge_types);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view) const { return InEdges(view, {}); }
|
||||
auto InEdges(storage::View view) const { return InEdges(view, {}); }
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view,
|
||||
const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const VertexAccessor &dest) const {
|
||||
auto maybe_result = impl_.InEdges(view, edge_types, &dest.impl_);
|
||||
if (maybe_result.HasError()) return maybe_result.GetError();
|
||||
|
||||
std::vector<EdgeAccessor> edges;
|
||||
edges.reserve((*maybe_result).edges.size());
|
||||
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
|
||||
[](auto const &edge) { return EdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
|
||||
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types, const VertexAccessor &dest) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
|
||||
auto maybe_edges = impl_.InEdges(view, edge_types, &dest.impl_);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view,
|
||||
const std::vector<storage::EdgeTypeId> &edge_types) const {
|
||||
auto maybe_result = impl_.OutEdges(view, edge_types);
|
||||
if (maybe_result.HasError()) return maybe_result.GetError();
|
||||
|
||||
std::vector<EdgeAccessor> edges;
|
||||
edges.reserve((*maybe_result).edges.size());
|
||||
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
|
||||
[](auto const &edge) { return EdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
|
||||
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, edge_types);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view) const { return OutEdges(view, {}); }
|
||||
auto OutEdges(storage::View view) const { return OutEdges(view, {}); }
|
||||
|
||||
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view,
|
||||
const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const VertexAccessor &dest) const {
|
||||
auto maybe_result = impl_.OutEdges(view, edge_types, &dest.impl_);
|
||||
if (maybe_result.HasError()) return maybe_result.GetError();
|
||||
|
||||
std::vector<EdgeAccessor> edges;
|
||||
edges.reserve((*maybe_result).edges.size());
|
||||
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
|
||||
[](auto const &edge) { return EdgeAccessor(edge); });
|
||||
|
||||
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
|
||||
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
|
||||
const VertexAccessor &dest) const
|
||||
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
|
||||
auto maybe_edges = impl_.OutEdges(view, edge_types, &dest.impl_);
|
||||
if (maybe_edges.HasError()) return maybe_edges.GetError();
|
||||
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
|
||||
}
|
||||
|
||||
storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); }
|
||||
@@ -228,12 +206,6 @@ inline VertexAccessor EdgeAccessor::To() const { return VertexAccessor(impl_.ToV
|
||||
|
||||
inline VertexAccessor EdgeAccessor::From() const { return VertexAccessor(impl_.FromVertex()); }
|
||||
|
||||
inline VertexAccessor EdgeAccessor::DeletedEdgeToVertex() const { return VertexAccessor(impl_.DeletedEdgeToVertex()); }
|
||||
|
||||
inline VertexAccessor EdgeAccessor::DeletedEdgeFromVertex() const {
|
||||
return VertexAccessor(impl_.DeletedEdgeFromVertex());
|
||||
}
|
||||
|
||||
inline bool EdgeAccessor::IsCycle() const { return To() == From(); }
|
||||
|
||||
class SubgraphVertexAccessor final {
|
||||
@@ -270,24 +242,15 @@ class SubgraphVertexAccessor final {
|
||||
|
||||
storage::Gid Gid() const noexcept { return impl_.Gid(); }
|
||||
|
||||
storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); }
|
||||
|
||||
storage::Result<size_t> OutDegree(storage::View view) const { return impl_.OutDegree(view); }
|
||||
|
||||
storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) {
|
||||
return impl_.SetProperty(key, value);
|
||||
}
|
||||
|
||||
storage::Result<std::vector<std::tuple<storage::PropertyId, storage::PropertyValue, storage::PropertyValue>>>
|
||||
UpdateProperties(std::map<storage::PropertyId, storage::PropertyValue> &properties) const {
|
||||
return impl_.UpdateProperties(properties);
|
||||
}
|
||||
|
||||
VertexAccessor GetVertexAccessor() const;
|
||||
};
|
||||
} // namespace memgraph::query
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<memgraph::query::VertexAccessor> {
|
||||
size_t operator()(const memgraph::query::VertexAccessor &v) const { return std::hash<decltype(v.impl_)>{}(v.impl_); }
|
||||
@@ -315,17 +278,17 @@ class VerticesIterable final {
|
||||
it_;
|
||||
|
||||
public:
|
||||
explicit Iterator(storage::VerticesIterable::Iterator it) : it_(std::move(it)) {}
|
||||
explicit Iterator(storage::VerticesIterable::Iterator it) : it_(it) {}
|
||||
explicit Iterator(std::unordered_set<VertexAccessor, std::hash<VertexAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<VertexAccessor>>::iterator it)
|
||||
: it_(it) {}
|
||||
|
||||
VertexAccessor operator*() const {
|
||||
return std::visit([](auto &it_) { return VertexAccessor(*it_); }, it_);
|
||||
return std::visit([](auto it_) { return VertexAccessor(*it_); }, it_);
|
||||
}
|
||||
|
||||
Iterator &operator++() {
|
||||
std::visit([](auto &it_) { ++it_; }, it_);
|
||||
std::visit([this](auto it_) { this->it_ = ++it_; }, it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -373,26 +336,6 @@ class DbAccessor final {
|
||||
|
||||
void FinalizeTransaction() { accessor_->FinalizeTransaction(); }
|
||||
|
||||
void TrackThreadAllocations(const char *thread_id) {
|
||||
memgraph::memory::UpdateThreadToTransactionId(thread_id, *accessor_->GetTransactionId());
|
||||
auto arena = memgraph::memory::GetArenaForThread();
|
||||
memgraph::memory::AddTrackingOnArena(arena);
|
||||
}
|
||||
|
||||
void TrackCurrentThreadAllocations() {
|
||||
memgraph::memory::AddTrackingsOnCurrentThread(*accessor_->GetTransactionId());
|
||||
}
|
||||
|
||||
void UntrackThreadAllocations(const char *thread_id) {
|
||||
memgraph::memory::ResetThreadToTransactionId(thread_id);
|
||||
auto arena = memgraph::memory::GetArenaForThread();
|
||||
memgraph::memory::RemoveTrackingOnArena(arena);
|
||||
}
|
||||
|
||||
void UntrackCurrentThreadAllocations() { memgraph::memory::RemoveTrackingsOnCurrentThread(); }
|
||||
|
||||
std::optional<uint64_t> GetTransactionId() { return accessor_->GetTransactionId(); }
|
||||
|
||||
VerticesIterable Vertices(storage::View view) { return VerticesIterable(accessor_->Vertices(view)); }
|
||||
|
||||
VerticesIterable Vertices(storage::View view, storage::LabelId label) {
|
||||
@@ -427,18 +370,6 @@ class DbAccessor final {
|
||||
return EdgeAccessor(*maybe_edge);
|
||||
}
|
||||
|
||||
storage::Result<EdgeAccessor> EdgeSetFrom(EdgeAccessor *edge, VertexAccessor *new_from) {
|
||||
auto changed_edge = accessor_->EdgeSetFrom(&edge->impl_, &new_from->impl_);
|
||||
if (changed_edge.HasError()) return storage::Result<EdgeAccessor>(changed_edge.GetError());
|
||||
return EdgeAccessor(*changed_edge);
|
||||
}
|
||||
|
||||
storage::Result<EdgeAccessor> EdgeSetTo(EdgeAccessor *edge, VertexAccessor *new_to) {
|
||||
auto changed_edge = accessor_->EdgeSetTo(&edge->impl_, &new_to->impl_);
|
||||
if (changed_edge.HasError()) return storage::Result<EdgeAccessor>(changed_edge.GetError());
|
||||
return EdgeAccessor(*changed_edge);
|
||||
}
|
||||
|
||||
storage::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge) {
|
||||
auto res = accessor_->DeleteEdge(&edge->impl_);
|
||||
if (res.HasError()) {
|
||||
@@ -473,8 +404,8 @@ class DbAccessor final {
|
||||
|
||||
std::vector<EdgeAccessor> deleted_edges;
|
||||
deleted_edges.reserve(edges.size());
|
||||
std::ranges::transform(edges, std::back_inserter(deleted_edges),
|
||||
[](const auto &deleted_edge) { return EdgeAccessor{deleted_edge}; });
|
||||
std::transform(edges.begin(), edges.end(), std::back_inserter(deleted_edges),
|
||||
[](const auto &deleted_edge) { return EdgeAccessor{deleted_edge}; });
|
||||
|
||||
return std::make_optional<ReturnType>(vertex, std::move(deleted_edges));
|
||||
}
|
||||
@@ -493,53 +424,6 @@ class DbAccessor final {
|
||||
return std::make_optional<VertexAccessor>(*value);
|
||||
}
|
||||
|
||||
storage::Result<std::optional<std::pair<std::vector<VertexAccessor>, std::vector<EdgeAccessor>>>> DetachDelete(
|
||||
std::vector<VertexAccessor> nodes, std::vector<EdgeAccessor> edges, bool detach) {
|
||||
using ReturnType = std::pair<std::vector<VertexAccessor>, std::vector<EdgeAccessor>>;
|
||||
|
||||
std::vector<storage::VertexAccessor *> nodes_impl;
|
||||
std::vector<storage::EdgeAccessor *> edges_impl;
|
||||
|
||||
nodes_impl.reserve(nodes.size());
|
||||
edges_impl.reserve(edges.size());
|
||||
|
||||
for (auto &vertex_accessor : nodes) {
|
||||
accessor_->PrefetchOutEdges(vertex_accessor.impl_);
|
||||
accessor_->PrefetchInEdges(vertex_accessor.impl_);
|
||||
|
||||
nodes_impl.push_back(&vertex_accessor.impl_);
|
||||
}
|
||||
|
||||
for (auto &edge_accessor : edges) {
|
||||
edges_impl.push_back(&edge_accessor.impl_);
|
||||
}
|
||||
|
||||
auto res = accessor_->DetachDelete(std::move(nodes_impl), std::move(edges_impl), detach);
|
||||
if (res.HasError()) {
|
||||
return res.GetError();
|
||||
}
|
||||
|
||||
const auto &value = res.GetValue();
|
||||
if (!value) {
|
||||
return std::optional<ReturnType>{};
|
||||
}
|
||||
|
||||
const auto &[val_vertices, val_edges] = *value;
|
||||
|
||||
std::vector<VertexAccessor> deleted_vertices;
|
||||
std::vector<EdgeAccessor> deleted_edges;
|
||||
|
||||
deleted_vertices.reserve(val_vertices.size());
|
||||
deleted_edges.reserve(val_edges.size());
|
||||
|
||||
std::ranges::transform(val_vertices, std::back_inserter(deleted_vertices),
|
||||
[](const auto &deleted_vertex) { return VertexAccessor{deleted_vertex}; });
|
||||
std::ranges::transform(val_edges, std::back_inserter(deleted_edges),
|
||||
[](const auto &deleted_edge) { return EdgeAccessor{deleted_edge}; });
|
||||
|
||||
return std::make_optional<ReturnType>(std::move(deleted_vertices), std::move(deleted_edges));
|
||||
}
|
||||
|
||||
storage::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
|
||||
|
||||
storage::LabelId NameToLabel(const std::string_view name) { return accessor_->NameToLabel(name); }
|
||||
@@ -554,7 +438,7 @@ class DbAccessor final {
|
||||
|
||||
void AdvanceCommand() { accessor_->AdvanceCommand(); }
|
||||
|
||||
utils::BasicResult<storage::StorageManipulationError, void> Commit() { return accessor_->Commit(); }
|
||||
utils::BasicResult<storage::StorageDataManipulationError, void> Commit() { return accessor_->Commit(); }
|
||||
|
||||
void Abort() { accessor_->Abort(); }
|
||||
|
||||
@@ -575,12 +459,20 @@ class DbAccessor final {
|
||||
return accessor_->GetIndexStats(label, property);
|
||||
}
|
||||
|
||||
std::vector<std::pair<storage::LabelId, storage::PropertyId>> DeleteLabelPropertyIndexStats(
|
||||
const storage::LabelId &label) {
|
||||
return accessor_->DeleteLabelPropertyIndexStats(label);
|
||||
std::vector<std::pair<storage::LabelId, storage::PropertyId>> ClearLabelPropertyIndexStats() {
|
||||
return accessor_->ClearLabelPropertyIndexStats();
|
||||
}
|
||||
|
||||
bool DeleteLabelIndexStats(const storage::LabelId &label) { return accessor_->DeleteLabelIndexStats(label); }
|
||||
std::vector<storage::LabelId> ClearLabelIndexStats() { return accessor_->ClearLabelIndexStats(); }
|
||||
|
||||
std::vector<std::pair<storage::LabelId, storage::PropertyId>> DeleteLabelPropertyIndexStats(
|
||||
const std::span<std::string> labels) {
|
||||
return accessor_->DeleteLabelPropertyIndexStats(labels);
|
||||
}
|
||||
|
||||
std::vector<storage::LabelId> DeleteLabelIndexStats(const std::span<std::string> labels) {
|
||||
return accessor_->DeleteLabelIndexStats(labels);
|
||||
}
|
||||
|
||||
void SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats) {
|
||||
accessor_->SetIndexStats(label, stats);
|
||||
@@ -615,44 +507,6 @@ class DbAccessor final {
|
||||
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
|
||||
|
||||
const std::string &id() const { return accessor_->id(); }
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> CreateIndex(storage::LabelId label) {
|
||||
return accessor_->CreateIndex(label);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> CreateIndex(storage::LabelId label,
|
||||
storage::PropertyId property) {
|
||||
return accessor_->CreateIndex(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> DropIndex(storage::LabelId label) {
|
||||
return accessor_->DropIndex(label);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> DropIndex(storage::LabelId label,
|
||||
storage::PropertyId property) {
|
||||
return accessor_->DropIndex(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
|
||||
storage::LabelId label, storage::PropertyId property) {
|
||||
return accessor_->CreateExistenceConstraint(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageExistenceConstraintDroppingError, void> DropExistenceConstraint(
|
||||
storage::LabelId label, storage::PropertyId property) {
|
||||
return accessor_->DropExistenceConstraint(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageUniqueConstraintDefinitionError, storage::UniqueConstraints::CreationStatus>
|
||||
CreateUniqueConstraint(storage::LabelId label, const std::set<storage::PropertyId> &properties) {
|
||||
return accessor_->CreateUniqueConstraint(label, properties);
|
||||
}
|
||||
|
||||
storage::UniqueConstraints::DeletionStatus DropUniqueConstraint(storage::LabelId label,
|
||||
const std::set<storage::PropertyId> &properties) {
|
||||
return accessor_->DropUniqueConstraint(label, properties);
|
||||
}
|
||||
};
|
||||
|
||||
class SubgraphDbAccessor final {
|
||||
@@ -664,14 +518,6 @@ class SubgraphDbAccessor final {
|
||||
|
||||
static SubgraphDbAccessor *MakeSubgraphDbAccessor(DbAccessor *db_accessor, Graph *graph);
|
||||
|
||||
void TrackThreadAllocations(const char *thread_id);
|
||||
|
||||
void TrackCurrentThreadAllocations();
|
||||
|
||||
void UntrackThreadAllocations(const char *thread_id);
|
||||
|
||||
void UntrackCurrentThreadAllocations();
|
||||
|
||||
storage::PropertyId NameToProperty(std::string_view name);
|
||||
|
||||
storage::LabelId NameToLabel(std::string_view name);
|
||||
@@ -693,10 +539,6 @@ class SubgraphDbAccessor final {
|
||||
storage::Result<EdgeAccessor> InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,
|
||||
const storage::EdgeTypeId &edge_type);
|
||||
|
||||
storage::Result<EdgeAccessor> EdgeSetFrom(EdgeAccessor *edge, SubgraphVertexAccessor *new_from);
|
||||
|
||||
storage::Result<EdgeAccessor> EdgeSetTo(EdgeAccessor *edge, SubgraphVertexAccessor *new_to);
|
||||
|
||||
storage::Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachRemoveVertex(
|
||||
SubgraphVertexAccessor *vertex_accessor);
|
||||
|
||||
|
||||
@@ -487,8 +487,8 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
|
||||
}
|
||||
auto &maybe_edges = *maybe_edge_iterable;
|
||||
MG_ASSERT(maybe_edges.HasValue(), "Invalid database state!");
|
||||
auto current_edge_iter = maybe_current_edge_iter ? *maybe_current_edge_iter : maybe_edges->edges.begin();
|
||||
for (; current_edge_iter != maybe_edges->edges.end() && (!n || local_counter < *n); ++current_edge_iter) {
|
||||
auto current_edge_iter = maybe_current_edge_iter ? *maybe_current_edge_iter : maybe_edges->begin();
|
||||
for (; current_edge_iter != maybe_edges->end() && (!n || local_counter < *n); ++current_edge_iter) {
|
||||
std::ostringstream os;
|
||||
DumpEdge(&os, dba_, *current_edge_iter);
|
||||
stream->Result({TypedValue(os.str())});
|
||||
@@ -496,7 +496,7 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
|
||||
++local_counter;
|
||||
}
|
||||
|
||||
if (current_edge_iter != maybe_edges->edges.end()) {
|
||||
if (current_edge_iter != maybe_edges->end()) {
|
||||
maybe_current_edge_iter.emplace(current_edge_iter);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -38,7 +38,7 @@ struct PullPlanDump {
|
||||
using VertexAccessorIterableIterator = decltype(std::declval<VertexAccessorIterable>().begin());
|
||||
|
||||
using EdgeAccessorIterable = decltype(std::declval<VertexAccessor>().OutEdges(storage::View::OLD));
|
||||
using EdgeAccessorIterableIterator = decltype(std::declval<EdgeAccessorIterable>().GetValue().edges.begin());
|
||||
using EdgeAccessorIterableIterator = decltype(std::declval<EdgeAccessorIterable>().GetValue().begin());
|
||||
|
||||
VertexAccessorIterable vertices_iterable_;
|
||||
bool internal_index_created_ = false;
|
||||
|
||||
@@ -158,17 +158,9 @@ class ExplicitTransactionUsageException : public QueryRuntimeException {
|
||||
using QueryRuntimeException::QueryRuntimeException;
|
||||
};
|
||||
|
||||
class DatabaseContextRequiredException : public QueryRuntimeException {
|
||||
public:
|
||||
using QueryRuntimeException::QueryRuntimeException;
|
||||
};
|
||||
|
||||
class WriteVertexOperationInEdgeImportModeException : public QueryException {
|
||||
public:
|
||||
WriteVertexOperationInEdgeImportModeException()
|
||||
: QueryException("Write operations on vertices are forbidden while the edge import mode is active.") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* An exception for serialization error
|
||||
*/
|
||||
class TransactionSerializationException : public QueryException {
|
||||
public:
|
||||
using QueryException::QueryException;
|
||||
@@ -279,12 +271,6 @@ class StorageModeModificationInMulticommandTxException : public QueryException {
|
||||
: QueryException("Storage mode cannot be modified in multicommand transactions.") {}
|
||||
};
|
||||
|
||||
class EdgeImportModeModificationInMulticommandTxException : public QueryException {
|
||||
public:
|
||||
EdgeImportModeModificationInMulticommandTxException()
|
||||
: QueryException("Edge import mode cannot be modified in multicommand transactions.") {}
|
||||
};
|
||||
|
||||
class CreateSnapshotInMulticommandTxException final : public QueryException {
|
||||
public:
|
||||
CreateSnapshotInMulticommandTxException()
|
||||
@@ -296,12 +282,6 @@ class CreateSnapshotDisabledOnDiskStorage final : public QueryException {
|
||||
CreateSnapshotDisabledOnDiskStorage() : QueryException("In the on-disk storage mode data is already persistent.") {}
|
||||
};
|
||||
|
||||
class EdgeImportModeQueryDisabledOnDiskStorage final : public QueryException {
|
||||
public:
|
||||
EdgeImportModeQueryDisabledOnDiskStorage()
|
||||
: QueryException("Edge import mode is only allowed for on-disk storage mode.") {}
|
||||
};
|
||||
|
||||
class SettingConfigInMulticommandTxException final : public QueryException {
|
||||
public:
|
||||
SettingConfigInMulticommandTxException()
|
||||
|
||||
@@ -223,11 +223,7 @@ constexpr utils::TypeInfo query::Unwind::kType{utils::TypeId::AST_UNWIND, "Unwin
|
||||
|
||||
constexpr utils::TypeInfo query::AuthQuery::kType{utils::TypeId::AST_AUTH_QUERY, "AuthQuery", &query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::DatabaseInfoQuery::kType{utils::TypeId::AST_DATABASE_INFO_QUERY, "DatabaseInfoQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::SystemInfoQuery::kType{utils::TypeId::AST_SYSTEM_INFO_QUERY, "SystemInfoQuery",
|
||||
&query::Query::kType};
|
||||
constexpr utils::TypeInfo query::InfoQuery::kType{utils::TypeId::AST_INFO_QUERY, "InfoQuery", &query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::Constraint::kType{utils::TypeId::AST_CONSTRAINT, "Constraint", nullptr};
|
||||
|
||||
@@ -289,8 +285,4 @@ constexpr utils::TypeInfo query::MultiDatabaseQuery::kType{utils::TypeId::AST_MU
|
||||
|
||||
constexpr utils::TypeInfo query::ShowDatabasesQuery::kType{utils::TypeId::AST_SHOW_DATABASES, "ShowDatabasesQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::EdgeImportModeQuery::kType{utils::TypeId::AST_EDGE_IMPORT_MODE_QUERY,
|
||||
"EdgeImportModeQuery", &query::Query::kType};
|
||||
|
||||
} // namespace memgraph
|
||||
|
||||
@@ -1105,8 +1105,6 @@ class MapProjectionLiteral : public memgraph::query::BaseLiteral {
|
||||
DEFVISITABLE(ExpressionVisitor<void>);
|
||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
||||
if (visitor.PreVisit(*this)) {
|
||||
map_variable_->Accept(visitor);
|
||||
|
||||
for (auto pair : elements_) {
|
||||
if (!pair.second) continue;
|
||||
|
||||
@@ -1186,8 +1184,6 @@ class PropertyLookup : public memgraph::query::Expression {
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class EvaluationMode { GET_OWN_PROPERTY, GET_ALL_PROPERTIES };
|
||||
|
||||
PropertyLookup() = default;
|
||||
|
||||
DEFVISITABLE(ExpressionVisitor<TypedValue>);
|
||||
@@ -1202,13 +1198,11 @@ class PropertyLookup : public memgraph::query::Expression {
|
||||
|
||||
memgraph::query::Expression *expression_{nullptr};
|
||||
memgraph::query::PropertyIx property_;
|
||||
memgraph::query::PropertyLookup::EvaluationMode evaluation_mode_{EvaluationMode::GET_OWN_PROPERTY};
|
||||
|
||||
PropertyLookup *Clone(AstStorage *storage) const override {
|
||||
PropertyLookup *object = storage->Create<PropertyLookup>();
|
||||
object->expression_ = expression_ ? expression_->Clone(storage) : nullptr;
|
||||
object->property_ = storage->GetPropertyIx(property_.name);
|
||||
object->evaluation_mode_ = evaluation_mode_;
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -2897,37 +2891,19 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {AuthQuery::Privilege::
|
||||
AuthQuery::Privilege::MULTI_DATABASE_EDIT,
|
||||
AuthQuery::Privilege::MULTI_DATABASE_USE};
|
||||
|
||||
class DatabaseInfoQuery : public memgraph::query::Query {
|
||||
class InfoQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class InfoType { INDEX, CONSTRAINT };
|
||||
enum class InfoType { STORAGE, INDEX, CONSTRAINT, BUILD };
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
|
||||
memgraph::query::DatabaseInfoQuery::InfoType info_type_;
|
||||
memgraph::query::InfoQuery::InfoType info_type_;
|
||||
|
||||
DatabaseInfoQuery *Clone(AstStorage *storage) const override {
|
||||
DatabaseInfoQuery *object = storage->Create<DatabaseInfoQuery>();
|
||||
object->info_type_ = info_type_;
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
class SystemInfoQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class InfoType { STORAGE, BUILD };
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
|
||||
memgraph::query::SystemInfoQuery::InfoType info_type_;
|
||||
|
||||
SystemInfoQuery *Clone(AstStorage *storage) const override {
|
||||
SystemInfoQuery *object = storage->Create<SystemInfoQuery>();
|
||||
InfoQuery *Clone(AstStorage *storage) const override {
|
||||
InfoQuery *object = storage->Create<InfoQuery>();
|
||||
object->info_type_ = info_type_;
|
||||
return object;
|
||||
}
|
||||
@@ -3027,29 +3003,6 @@ class ReplicationQuery : public memgraph::query::Query {
|
||||
friend class AstStorage;
|
||||
};
|
||||
|
||||
class EdgeImportModeQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class Status { ACTIVE, INACTIVE };
|
||||
|
||||
EdgeImportModeQuery() = default;
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
|
||||
memgraph::query::EdgeImportModeQuery::Status status_;
|
||||
|
||||
EdgeImportModeQuery *Clone(AstStorage *storage) const override {
|
||||
auto *object = storage->Create<EdgeImportModeQuery>();
|
||||
object->status_ = status_;
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AstStorage;
|
||||
};
|
||||
|
||||
class LockPathQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
|
||||
@@ -82,8 +82,7 @@ class AuthQuery;
|
||||
class ExplainQuery;
|
||||
class ProfileQuery;
|
||||
class IndexQuery;
|
||||
class DatabaseInfoQuery;
|
||||
class SystemInfoQuery;
|
||||
class InfoQuery;
|
||||
class ConstraintQuery;
|
||||
class RegexMatch;
|
||||
class DumpQuery;
|
||||
@@ -106,7 +105,6 @@ class TransactionQueueQuery;
|
||||
class Exists;
|
||||
class MultiDatabaseQuery;
|
||||
class ShowDatabasesQuery;
|
||||
class EdgeImportModeQuery;
|
||||
|
||||
using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||
@@ -141,10 +139,10 @@ class ExpressionVisitor
|
||||
|
||||
template <class TResult>
|
||||
class QueryVisitor
|
||||
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, DatabaseInfoQuery,
|
||||
SystemInfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
|
||||
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery, StreamQuery,
|
||||
SettingQuery, VersionQuery, ShowConfigQuery, TransactionQueueQuery, StorageModeQuery,
|
||||
AnalyzeGraphQuery, MultiDatabaseQuery, ShowDatabasesQuery, EdgeImportModeQuery> {};
|
||||
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
|
||||
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
|
||||
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery,
|
||||
ShowConfigQuery, TransactionQueueQuery, StorageModeQuery, AnalyzeGraphQuery,
|
||||
MultiDatabaseQuery, ShowDatabasesQuery> {};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -112,36 +112,25 @@ antlrcpp::Any CypherMainVisitor::visitProfileQuery(MemgraphCypher::ProfileQueryC
|
||||
return profile_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitDatabaseInfoQuery(MemgraphCypher::DatabaseInfoQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 2, "DatabaseInfoQuery should have exactly two children!");
|
||||
auto *info_query = storage_->Create<DatabaseInfoQuery>();
|
||||
query_ = info_query;
|
||||
if (ctx->indexInfo()) {
|
||||
info_query->info_type_ = DatabaseInfoQuery::InfoType::INDEX;
|
||||
return info_query;
|
||||
}
|
||||
if (ctx->constraintInfo()) {
|
||||
info_query->info_type_ = DatabaseInfoQuery::InfoType::CONSTRAINT;
|
||||
return info_query;
|
||||
}
|
||||
// Should never get here
|
||||
throw utils::NotYetImplemented("Database info query: '{}'", ctx->getText());
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitSystemInfoQuery(MemgraphCypher::SystemInfoQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 2, "SystemInfoQuery should have exactly two children!");
|
||||
auto *info_query = storage_->Create<SystemInfoQuery>();
|
||||
antlrcpp::Any CypherMainVisitor::visitInfoQuery(MemgraphCypher::InfoQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 2, "InfoQuery should have exactly two children!");
|
||||
auto *info_query = storage_->Create<InfoQuery>();
|
||||
query_ = info_query;
|
||||
if (ctx->storageInfo()) {
|
||||
info_query->info_type_ = SystemInfoQuery::InfoType::STORAGE;
|
||||
info_query->info_type_ = InfoQuery::InfoType::STORAGE;
|
||||
return info_query;
|
||||
}
|
||||
if (ctx->buildInfo()) {
|
||||
info_query->info_type_ = SystemInfoQuery::InfoType::BUILD;
|
||||
} else if (ctx->indexInfo()) {
|
||||
info_query->info_type_ = InfoQuery::InfoType::INDEX;
|
||||
return info_query;
|
||||
} else if (ctx->constraintInfo()) {
|
||||
info_query->info_type_ = InfoQuery::InfoType::CONSTRAINT;
|
||||
return info_query;
|
||||
} else if (ctx->buildInfo()) {
|
||||
info_query->info_type_ = InfoQuery::InfoType::BUILD;
|
||||
return info_query;
|
||||
} else {
|
||||
throw utils::NotYetImplemented("Info query: '{}'", ctx->getText());
|
||||
}
|
||||
// Should never get here
|
||||
throw utils::NotYetImplemented("System info query: '{}'", ctx->getText());
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitConstraintQuery(MemgraphCypher::ConstraintQueryContext *ctx) {
|
||||
@@ -280,17 +269,6 @@ antlrcpp::Any CypherMainVisitor::visitReplicationQuery(MemgraphCypher::Replicati
|
||||
return replication_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitEdgeImportModeQuery(MemgraphCypher::EdgeImportModeQueryContext *ctx) {
|
||||
auto *edge_import_mode_query = storage_->Create<EdgeImportModeQuery>();
|
||||
if (ctx->ACTIVE()) {
|
||||
edge_import_mode_query->status_ = EdgeImportModeQuery::Status::ACTIVE;
|
||||
} else {
|
||||
edge_import_mode_query->status_ = EdgeImportModeQuery::Status::INACTIVE;
|
||||
}
|
||||
query_ = edge_import_mode_query;
|
||||
return edge_import_mode_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitSetReplicationRole(MemgraphCypher::SetReplicationRoleContext *ctx) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::SET_REPLICATION_ROLE;
|
||||
|
||||
@@ -157,14 +157,9 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
antlrcpp::Any visitProfileQuery(MemgraphCypher::ProfileQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return DatabaseInfoQuery*
|
||||
* @return InfoQuery*
|
||||
*/
|
||||
antlrcpp::Any visitDatabaseInfoQuery(MemgraphCypher::DatabaseInfoQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return SystemInfoQuery*
|
||||
*/
|
||||
antlrcpp::Any visitSystemInfoQuery(MemgraphCypher::SystemInfoQueryContext *ctx) override;
|
||||
antlrcpp::Any visitInfoQuery(MemgraphCypher::InfoQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return Constraint
|
||||
@@ -201,11 +196,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitReplicationQuery(MemgraphCypher::ReplicationQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return EdgeImportMode*
|
||||
*/
|
||||
antlrcpp::Any visitEdgeImportModeQuery(MemgraphCypher::EdgeImportModeQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return ReplicationQuery*
|
||||
*/
|
||||
|
||||
@@ -27,8 +27,7 @@ query : cypherQuery
|
||||
| indexQuery
|
||||
| explainQuery
|
||||
| profileQuery
|
||||
| databaseInfoQuery
|
||||
| systemInfoQuery
|
||||
| infoQuery
|
||||
| constraintQuery
|
||||
;
|
||||
|
||||
@@ -49,9 +48,7 @@ constraintInfo : CONSTRAINT INFO ;
|
||||
|
||||
buildInfo : BUILD INFO ;
|
||||
|
||||
databaseInfoQuery : SHOW ( indexInfo | constraintInfo ) ;
|
||||
|
||||
systemInfoQuery : SHOW ( storageInfo | buildInfo ) ;
|
||||
infoQuery : SHOW ( storageInfo | indexInfo | constraintInfo | buildInfo) ;
|
||||
|
||||
explainQuery : EXPLAIN cypherQuery ;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ options { tokenVocab=MemgraphCypherLexer; }
|
||||
import Cypher ;
|
||||
|
||||
memgraphCypherKeyword : cypherKeyword
|
||||
| ACTIVE
|
||||
| AFTER
|
||||
| ALTER
|
||||
| ANALYZE
|
||||
@@ -49,7 +48,6 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| DENY
|
||||
| DROP
|
||||
| DUMP
|
||||
| EDGE
|
||||
| EDGE_TYPES
|
||||
| EXECUTE
|
||||
| FOR
|
||||
@@ -62,11 +60,9 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| HEADER
|
||||
| IDENTIFIED
|
||||
| NULLIF
|
||||
| IMPORT
|
||||
| INACTIVE
|
||||
| ISOLATION
|
||||
| IN_MEMORY_ANALYTICAL
|
||||
| IN_MEMORY_TRANSACTIONAL
|
||||
| ISOLATION
|
||||
| KAFKA
|
||||
| LABELS
|
||||
| LEVEL
|
||||
@@ -128,8 +124,7 @@ query : cypherQuery
|
||||
| indexQuery
|
||||
| explainQuery
|
||||
| profileQuery
|
||||
| databaseInfoQuery
|
||||
| systemInfoQuery
|
||||
| infoQuery
|
||||
| constraintQuery
|
||||
| authQuery
|
||||
| dumpQuery
|
||||
@@ -148,7 +143,6 @@ query : cypherQuery
|
||||
| transactionQueueQuery
|
||||
| multiDatabaseQuery
|
||||
| showDatabases
|
||||
| edgeImportModeQuery
|
||||
;
|
||||
|
||||
authQuery : createRole
|
||||
@@ -481,5 +475,3 @@ useDatabase : USE DATABASE databaseName ;
|
||||
dropDatabase : DROP DATABASE databaseName ;
|
||||
|
||||
showDatabases: SHOW DATABASES ;
|
||||
|
||||
edgeImportModeQuery : EDGE IMPORT MODE ( ACTIVE | INACTIVE ) ;
|
||||
|
||||
@@ -23,7 +23,6 @@ lexer grammar MemgraphCypherLexer ;
|
||||
|
||||
import CypherLexer ;
|
||||
|
||||
ACTIVE : A C T I V E ;
|
||||
AFTER : A F T E R ;
|
||||
ALTER : A L T E R ;
|
||||
ANALYZE : A N A L Y Z E ;
|
||||
@@ -56,7 +55,6 @@ DIRECTORY : D I R E C T O R Y ;
|
||||
DROP : D R O P ;
|
||||
DUMP : D U M P ;
|
||||
DURABILITY : D U R A B I L I T Y ;
|
||||
EDGE : E D G E ;
|
||||
EDGE_TYPES : E D G E UNDERSCORE T Y P E S ;
|
||||
EXECUTE : E X E C U T E ;
|
||||
FOR : F O R ;
|
||||
@@ -71,11 +69,9 @@ GRANTS : G R A N T S ;
|
||||
HEADER : H E A D E R ;
|
||||
IDENTIFIED : I D E N T I F I E D ;
|
||||
IGNORE : I G N O R E ;
|
||||
IMPORT : I M P O R T ;
|
||||
INACTIVE : I N A C T I V E ;
|
||||
ISOLATION : I S O L A T I O N ;
|
||||
IN_MEMORY_ANALYTICAL : I N UNDERSCORE M E M O R Y UNDERSCORE A N A L Y T I C A L ;
|
||||
IN_MEMORY_TRANSACTIONAL : I N UNDERSCORE M E M O R Y UNDERSCORE T R A N S A C T I O N A L ;
|
||||
ISOLATION : I S O L A T I O N ;
|
||||
KAFKA : K A F K A ;
|
||||
LABELS : L A B E L S ;
|
||||
LEVEL : L E V E L ;
|
||||
|
||||
@@ -35,14 +35,18 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
|
||||
void Visit(ProfileQuery &query) override { query.cypher_query_->Accept(dynamic_cast<QueryVisitor &>(*this)); }
|
||||
|
||||
void Visit(DatabaseInfoQuery &info_query) override {
|
||||
void Visit(InfoQuery &info_query) override {
|
||||
switch (info_query.info_type_) {
|
||||
case DatabaseInfoQuery::InfoType::INDEX:
|
||||
case InfoQuery::InfoType::INDEX:
|
||||
// TODO: This should be INDEX | STATS, but we don't have support for
|
||||
// *or* with privileges.
|
||||
AddPrivilege(AuthQuery::Privilege::INDEX);
|
||||
break;
|
||||
case DatabaseInfoQuery::InfoType::CONSTRAINT:
|
||||
case InfoQuery::InfoType::STORAGE:
|
||||
case InfoQuery::InfoType::BUILD:
|
||||
AddPrivilege(AuthQuery::Privilege::STATS);
|
||||
break;
|
||||
case InfoQuery::InfoType::CONSTRAINT:
|
||||
// TODO: This should be CONSTRAINT | STATS, but we don't have support
|
||||
// for *or* with privileges.
|
||||
AddPrivilege(AuthQuery::Privilege::CONSTRAINT);
|
||||
@@ -50,15 +54,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(SystemInfoQuery &info_query) override {
|
||||
switch (info_query.info_type_) {
|
||||
case SystemInfoQuery::InfoType::STORAGE:
|
||||
case SystemInfoQuery::InfoType::BUILD:
|
||||
AddPrivilege(AuthQuery::Privilege::STATS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(ConstraintQuery &constraint_query) override { AddPrivilege(AuthQuery::Privilege::CONSTRAINT); }
|
||||
|
||||
void Visit(CypherQuery &query) override {
|
||||
@@ -92,8 +87,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
|
||||
void Visit(TransactionQueueQuery & /*transaction_queue_query*/) override {}
|
||||
|
||||
void Visit(EdgeImportModeQuery & /*edge_import_mode_query*/) override {}
|
||||
|
||||
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
|
||||
|
||||
void Visit(MultiDatabaseQuery &query) override {
|
||||
|
||||
@@ -400,29 +400,6 @@ SymbolGenerator::ReturnType SymbolGenerator::Visit(Identifier &ident) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PostVisit(MapLiteral &map_literal) {
|
||||
std::unordered_map<int32_t, PropertyLookup *> property_lookups{};
|
||||
|
||||
for (const auto &pair : map_literal.elements_) {
|
||||
if (pair.second->GetTypeInfo() != PropertyLookup::kType) continue;
|
||||
auto *property_lookup = static_cast<PropertyLookup *>(pair.second);
|
||||
if (property_lookup->expression_->GetTypeInfo() != Identifier::kType) continue;
|
||||
|
||||
auto symbol_pos = static_cast<Identifier *>(property_lookup->expression_)->symbol_pos_;
|
||||
try {
|
||||
auto *existing_property_lookup = property_lookups.at(symbol_pos);
|
||||
// If already there (no exception), update the original and current PropertyLookups
|
||||
existing_property_lookup->evaluation_mode_ = PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES;
|
||||
property_lookup->evaluation_mode_ = PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES;
|
||||
} catch (const std::out_of_range &) {
|
||||
// Otherwise, add the PropertyLookup to the map
|
||||
property_lookups.emplace(symbol_pos, property_lookup);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolGenerator::PreVisit(Aggregation &aggr) {
|
||||
auto &scope = scopes_.back();
|
||||
// Check if the aggregation can be used in this context. This check should
|
||||
|
||||
@@ -72,8 +72,6 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
|
||||
// Expressions
|
||||
ReturnType Visit(Identifier &) override;
|
||||
ReturnType Visit(PrimitiveLiteral &) override { return true; }
|
||||
bool PreVisit(MapLiteral &) override { return true; }
|
||||
bool PostVisit(MapLiteral &) override;
|
||||
ReturnType Visit(ParameterLookup &) override { return true; }
|
||||
bool PreVisit(Aggregation &) override;
|
||||
bool PostVisit(Aggregation &) override;
|
||||
|
||||
@@ -218,7 +218,7 @@ const trie::Trie kKeywords = {"union",
|
||||
"data",
|
||||
"directory",
|
||||
"lock",
|
||||
"unlock",
|
||||
"unlock"
|
||||
"build"};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -22,10 +22,9 @@ int64_t EvaluateInt(ExpressionEvaluator *evaluator, Expression *expr, const std:
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<size_t> EvaluateMemoryLimit(ExpressionVisitor<TypedValue> &eval, Expression *memory_limit,
|
||||
size_t memory_scale) {
|
||||
std::optional<size_t> EvaluateMemoryLimit(ExpressionEvaluator *eval, Expression *memory_limit, size_t memory_scale) {
|
||||
if (!memory_limit) return std::nullopt;
|
||||
auto limit_value = memory_limit->Accept(eval);
|
||||
auto limit_value = memory_limit->Accept(*eval);
|
||||
if (!limit_value.IsInt() || limit_value.ValueInt() <= 0)
|
||||
throw QueryRuntimeException("Memory limit must be a non-negative integer.");
|
||||
size_t limit = limit_value.ValueInt();
|
||||
|
||||
@@ -99,79 +99,12 @@ class ReferenceExpressionEvaluator : public ExpressionVisitor<TypedValue *> {
|
||||
UNSUCCESSFUL_VISIT(RegexMatch);
|
||||
UNSUCCESSFUL_VISIT(Exists);
|
||||
|
||||
#undef UNSUCCESSFUL_VISIT
|
||||
|
||||
private:
|
||||
Frame *frame_;
|
||||
const SymbolTable *symbol_table_;
|
||||
const EvaluationContext *ctx_;
|
||||
};
|
||||
|
||||
class PrimitiveLiteralExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
public:
|
||||
explicit PrimitiveLiteralExpressionEvaluator(EvaluationContext const &ctx) : ctx_(&ctx) {}
|
||||
using ExpressionVisitor<TypedValue>::Visit;
|
||||
TypedValue Visit(PrimitiveLiteral &literal) override {
|
||||
// TODO: no need to evaluate constants, we can write it to frame in one
|
||||
// of the previous phases.
|
||||
return TypedValue(literal.value_, ctx_->memory);
|
||||
}
|
||||
TypedValue Visit(ParameterLookup ¶m_lookup) override {
|
||||
return TypedValue(ctx_->parameters.AtTokenPosition(param_lookup.token_position_), ctx_->memory);
|
||||
}
|
||||
|
||||
#define INVALID_VISIT(expr_name) \
|
||||
TypedValue Visit(expr_name & /*expr*/) override { \
|
||||
DLOG_FATAL("Invalid expression type visited with PrimitiveLiteralExpressionEvaluator."); \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
INVALID_VISIT(NamedExpression)
|
||||
INVALID_VISIT(OrOperator)
|
||||
INVALID_VISIT(XorOperator)
|
||||
INVALID_VISIT(AndOperator)
|
||||
INVALID_VISIT(NotOperator)
|
||||
INVALID_VISIT(AdditionOperator)
|
||||
INVALID_VISIT(SubtractionOperator)
|
||||
INVALID_VISIT(MultiplicationOperator)
|
||||
INVALID_VISIT(DivisionOperator)
|
||||
INVALID_VISIT(ModOperator)
|
||||
INVALID_VISIT(NotEqualOperator)
|
||||
INVALID_VISIT(EqualOperator)
|
||||
INVALID_VISIT(LessOperator)
|
||||
INVALID_VISIT(GreaterOperator)
|
||||
INVALID_VISIT(LessEqualOperator)
|
||||
INVALID_VISIT(GreaterEqualOperator)
|
||||
INVALID_VISIT(InListOperator)
|
||||
INVALID_VISIT(SubscriptOperator)
|
||||
INVALID_VISIT(ListSlicingOperator)
|
||||
INVALID_VISIT(IfOperator)
|
||||
INVALID_VISIT(UnaryPlusOperator)
|
||||
INVALID_VISIT(UnaryMinusOperator)
|
||||
INVALID_VISIT(IsNullOperator)
|
||||
INVALID_VISIT(ListLiteral)
|
||||
INVALID_VISIT(MapLiteral)
|
||||
INVALID_VISIT(MapProjectionLiteral)
|
||||
INVALID_VISIT(PropertyLookup)
|
||||
INVALID_VISIT(AllPropertiesLookup)
|
||||
INVALID_VISIT(LabelsTest)
|
||||
INVALID_VISIT(Aggregation)
|
||||
INVALID_VISIT(Function)
|
||||
INVALID_VISIT(Reduce)
|
||||
INVALID_VISIT(Coalesce)
|
||||
INVALID_VISIT(Extract)
|
||||
INVALID_VISIT(All)
|
||||
INVALID_VISIT(Single)
|
||||
INVALID_VISIT(Any)
|
||||
INVALID_VISIT(None)
|
||||
INVALID_VISIT(Identifier)
|
||||
INVALID_VISIT(RegexMatch)
|
||||
INVALID_VISIT(Exists)
|
||||
|
||||
#undef INVALID_VISIT
|
||||
private:
|
||||
EvaluationContext const *ctx_;
|
||||
};
|
||||
class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
public:
|
||||
ExpressionEvaluator(Frame *frame, const SymbolTable &symbol_table, const EvaluationContext &ctx, DbAccessor *dba,
|
||||
@@ -546,35 +479,9 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue(ctx_->memory);
|
||||
case TypedValue::Type::Vertex:
|
||||
if (property_lookup.evaluation_mode_ == PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES) {
|
||||
auto symbol_pos = static_cast<Identifier *>(property_lookup.expression_)->symbol_pos_;
|
||||
if (!ctx_->property_lookups_cache.contains(symbol_pos)) {
|
||||
ctx_->property_lookups_cache.emplace(symbol_pos, GetAllProperties(expression_result_ptr->ValueVertex()));
|
||||
}
|
||||
|
||||
auto property_id = ctx_->properties[property_lookup.property_.ix];
|
||||
if (ctx_->property_lookups_cache[symbol_pos].contains(property_id)) {
|
||||
return TypedValue(ctx_->property_lookups_cache[symbol_pos][property_id], ctx_->memory);
|
||||
}
|
||||
return TypedValue(ctx_->memory);
|
||||
} else {
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
}
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueVertex(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Edge:
|
||||
if (property_lookup.evaluation_mode_ == PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES) {
|
||||
auto symbol_pos = static_cast<Identifier *>(property_lookup.expression_)->symbol_pos_;
|
||||
if (!ctx_->property_lookups_cache.contains(symbol_pos)) {
|
||||
ctx_->property_lookups_cache.emplace(symbol_pos, GetAllProperties(expression_result_ptr->ValueEdge()));
|
||||
}
|
||||
|
||||
auto property_id = ctx_->properties[property_lookup.property_.ix];
|
||||
if (ctx_->property_lookups_cache[symbol_pos].contains(property_id)) {
|
||||
return TypedValue(ctx_->property_lookups_cache[symbol_pos][property_id], ctx_->memory);
|
||||
}
|
||||
return TypedValue(ctx_->memory);
|
||||
} else {
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
}
|
||||
return TypedValue(GetProperty(expression_result_ptr->ValueEdge(), property_lookup.property_), ctx_->memory);
|
||||
case TypedValue::Type::Map: {
|
||||
auto &map = expression_result_ptr->ValueMap();
|
||||
auto found = map.find(property_lookup.property_.name.c_str());
|
||||
@@ -780,14 +687,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
|
||||
TypedValue Visit(MapLiteral &literal) override {
|
||||
TypedValue::TMap result(ctx_->memory);
|
||||
for (const auto &pair : literal.elements_) {
|
||||
result.emplace(pair.first.name, pair.second->Accept(*this));
|
||||
}
|
||||
|
||||
ctx_->property_lookups_cache.clear();
|
||||
// TODO Don’t clear the cache if there are remaining MapLiterals with PropertyLookups that read the same properties
|
||||
// from the same variable (symbol & value)
|
||||
|
||||
for (const auto &pair : literal.elements_) result.emplace(pair.first.name, pair.second->Accept(*this));
|
||||
return TypedValue(result, ctx_->memory);
|
||||
}
|
||||
|
||||
@@ -796,27 +696,20 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
|
||||
TypedValue::TMap result(ctx_->memory);
|
||||
TypedValue::TMap all_properties_lookup(ctx_->memory);
|
||||
|
||||
auto map_variable = literal.map_variable_->Accept(*this);
|
||||
if (map_variable.IsNull()) {
|
||||
return TypedValue(ctx_->memory);
|
||||
}
|
||||
|
||||
for (const auto &[property_key, property_value] : literal.elements_) {
|
||||
if (property_key.name == kAllPropertiesSelector.data()) {
|
||||
auto maybe_all_properties_lookup = property_value->Accept(*this);
|
||||
|
||||
if (maybe_all_properties_lookup.type() != TypedValue::Type::Map) {
|
||||
LOG_FATAL("Expected a map from AllPropertiesLookup, got {}.", maybe_all_properties_lookup.type());
|
||||
throw QueryRuntimeException("Expected a map from AllPropertiesLookup, got {}.",
|
||||
maybe_all_properties_lookup.type());
|
||||
}
|
||||
|
||||
all_properties_lookup = std::move(maybe_all_properties_lookup.ValueMap());
|
||||
continue;
|
||||
}
|
||||
|
||||
result.emplace(property_key.name, property_value->Accept(*this));
|
||||
}
|
||||
|
||||
if (!all_properties_lookup.empty()) result.merge(all_properties_lookup);
|
||||
|
||||
return TypedValue(result, ctx_->memory);
|
||||
@@ -1081,33 +974,6 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
}
|
||||
|
||||
private:
|
||||
template <class TRecordAccessor>
|
||||
std::map<storage::PropertyId, storage::PropertyValue> GetAllProperties(const TRecordAccessor &record_accessor) {
|
||||
auto maybe_props = record_accessor.Properties(view_);
|
||||
if (maybe_props.HasError() && maybe_props.GetError() == storage::Error::NONEXISTENT_OBJECT) {
|
||||
// This is a very nasty and temporary hack in order to make MERGE work.
|
||||
// The old storage had the following logic when returning an `OLD` view:
|
||||
// `return old ? old : new`. That means that if the `OLD` view didn't
|
||||
// exist, it returned the NEW view. With this hack we simulate that
|
||||
// behavior.
|
||||
// TODO (mferencevic, teon.banek): Remove once MERGE is reimplemented.
|
||||
maybe_props = record_accessor.Properties(storage::View::NEW);
|
||||
}
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
throw QueryRuntimeException("Trying to get properties from a deleted object.");
|
||||
case storage::Error::NONEXISTENT_OBJECT:
|
||||
throw query::QueryRuntimeException("Trying to get properties from an object that doesn't exist.");
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
throw QueryRuntimeException("Unexpected error when getting properties.");
|
||||
}
|
||||
}
|
||||
return *maybe_props;
|
||||
}
|
||||
|
||||
template <class TRecordAccessor>
|
||||
storage::PropertyValue GetProperty(const TRecordAccessor &record_accessor, PropertyIx prop) {
|
||||
auto maybe_prop = record_accessor.GetProperty(view_, ctx_->properties[prop.ix]);
|
||||
@@ -1180,7 +1046,6 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
/// @throw QueryRuntimeException if expression doesn't evaluate to an int.
|
||||
int64_t EvaluateInt(ExpressionEvaluator *evaluator, Expression *expr, const std::string &what);
|
||||
|
||||
std::optional<size_t> EvaluateMemoryLimit(ExpressionVisitor<TypedValue> &eval, Expression *memory_limit,
|
||||
size_t memory_scale);
|
||||
std::optional<size_t> EvaluateMemoryLimit(ExpressionEvaluator *eval, Expression *memory_limit, size_t memory_scale);
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user