Compare commits
100 Commits
optimize-d
...
add-transf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e71b9a99ae | ||
|
|
2aa6b9ba8f | ||
|
|
6c9cdef944 | ||
|
|
5c10fb1d51 | ||
|
|
e73b3feea3 | ||
|
|
2ed40e9f16 | ||
|
|
ced34bc79c | ||
|
|
2b3540119f | ||
|
|
3936668e13 | ||
|
|
476080bace | ||
|
|
8528822af1 | ||
|
|
7ecf970dcd | ||
|
|
6ad5463d72 | ||
|
|
b1d560737e | ||
|
|
b719f0744f | ||
|
|
440838c0e9 | ||
|
|
79a3c5af8e | ||
|
|
bf03b38e39 | ||
|
|
fd63944493 | ||
|
|
6694de2dfa | ||
|
|
5e5f4ffc5d | ||
|
|
060b9d1c16 | ||
|
|
29a505cb38 | ||
|
|
d4fcd745d2 | ||
|
|
58546a9fe1 | ||
|
|
ab56abf4ca | ||
|
|
1bd47318cd | ||
|
|
0403b67073 | ||
|
|
9e4babcdbb | ||
|
|
b094fdbadc | ||
|
|
07dea328d8 | ||
|
|
bd1852f407 | ||
|
|
9c51dbbb01 | ||
|
|
c0d4f5e0bc | ||
|
|
974a6e3027 | ||
|
|
d9464c6ffd | ||
|
|
312d01bd0c | ||
|
|
b6b32bec03 | ||
|
|
93992a275b | ||
|
|
b5413c6f82 | ||
|
|
09fd5939da | ||
|
|
02eab6ab9c | ||
|
|
9661c52179 | ||
|
|
e928eed028 | ||
|
|
eb5167dfef | ||
|
|
b952139973 | ||
|
|
28dbcd1545 | ||
|
|
d516e40841 | ||
|
|
a6ec81b179 | ||
|
|
c526ff2a8f | ||
|
|
5f509532f2 | ||
|
|
4b3ba908c7 | ||
|
|
030b554ffd | ||
|
|
4bc5d749b2 | ||
|
|
a426ef9cc3 | ||
|
|
60e167d676 | ||
|
|
3f8befde79 | ||
|
|
9355e58e73 | ||
|
|
476968e2c8 | ||
|
|
97183fb9da | ||
|
|
adb65b2fff | ||
|
|
3bf2cf65ab | ||
|
|
8f3f693f20 | ||
|
|
2e51e703c3 | ||
|
|
adf7533751 | ||
|
|
509183e985 | ||
|
|
1fe2190747 | ||
|
|
762fe6a65d | ||
|
|
271b1a5ddb | ||
|
|
260660f1dd | ||
|
|
e5350a011c | ||
|
|
7bf827bb1e | ||
|
|
5d13c281fa | ||
|
|
020273f475 | ||
|
|
4a99625287 | ||
|
|
5bbed6ef9a | ||
|
|
f0bac53e7b | ||
|
|
514fed51c4 | ||
|
|
2877c343e8 | ||
|
|
50a1d1abb3 | ||
|
|
e8850549d2 | ||
|
|
fd819cd099 | ||
|
|
60f4ffc6a1 | ||
|
|
bd2ec6374a | ||
|
|
c501f59a09 | ||
|
|
210bea83d4 | ||
|
|
57fe3463f2 | ||
|
|
53fcd8ac4d | ||
|
|
259cba5d43 | ||
|
|
285b409927 | ||
|
|
8ebab84324 | ||
|
|
3fd9ce4a33 | ||
|
|
be4eb95a98 | ||
|
|
903a9f4636 | ||
|
|
18bd02423a | ||
|
|
58c0c4cebb | ||
|
|
110ca3968c | ||
|
|
9072fb7703 | ||
|
|
2b7707a2f1 | ||
|
|
76ca019f31 |
20
.github/workflows/daily_benchmark.yaml
vendored
20
.github/workflows/daily_benchmark.yaml
vendored
@@ -67,7 +67,11 @@ jobs:
|
||||
- name: Run mgbench
|
||||
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 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
|
||||
|
||||
- name: Upload mgbench results
|
||||
run: |
|
||||
@@ -76,7 +80,19 @@ jobs:
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "mgbench" \
|
||||
--benchmark-results-path "../../tests/mgbench/benchmark_result.json" \
|
||||
--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" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
13
.github/workflows/diff.yaml
vendored
13
.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 --check-only --diff ${file}
|
||||
python3 -m isort --profile black --check-only --diff ${file}
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -229,6 +229,11 @@ 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.
|
||||
@@ -267,19 +272,21 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh
|
||||
source ve3/bin/activate
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
- 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
|
||||
|
||||
4
.github/workflows/package_all.yaml
vendored
4
.github/workflows/package_all.yaml
vendored
@@ -201,7 +201,7 @@ jobs:
|
||||
|
||||
debian-11-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v3
|
||||
@@ -218,7 +218,7 @@ jobs:
|
||||
|
||||
ubuntu-2204-arm:
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64, strange]
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
77
.github/workflows/performance_benchmarks.yaml
vendored
Normal file
77
.github/workflows/performance_benchmarks.yaml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
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 }}"
|
||||
4
.github/workflows/release_centos8.yaml
vendored
4
.github/workflows/release_centos8.yaml
vendored
@@ -266,8 +266,8 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh
|
||||
source ve3/bin/activate
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
|
||||
4
.github/workflows/release_debian10.yaml
vendored
4
.github/workflows/release_debian10.yaml
vendored
@@ -265,8 +265,8 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh
|
||||
source ve3/bin/activate
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
|
||||
4
.github/workflows/release_ubuntu2004.yaml
vendored
4
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -265,8 +265,8 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh
|
||||
source ve3/bin/activate
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -59,3 +59,5 @@ src/raft/storage_info_rpc_messages.hpp
|
||||
src/stats/stats_rpc_messages.hpp
|
||||
src/storage/distributed/rpc/concurrent_id_mapper_rpc_messages.hpp
|
||||
src/transactions/distributed/engine_rpc_messages.hpp
|
||||
/tests/manual/js/transaction_timeout/package-lock.json
|
||||
/tests/manual/js/transaction_timeout/node_modules/
|
||||
|
||||
@@ -15,7 +15,21 @@ repos:
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
args: ["--profile", "black"]
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v13.0.0
|
||||
hooks:
|
||||
- id: clang-format
|
||||
# - repo: local
|
||||
# hooks:
|
||||
# - id: clang-tidy
|
||||
# name: clang-tidy
|
||||
# description: Runs clang-tidy and checks for errors
|
||||
# entry: python ./tools/pre-commit/clang-tidy.py
|
||||
# language: python
|
||||
# files: ^src/
|
||||
# types: [c++, text]
|
||||
# fail_fast: true
|
||||
# require_serial: true
|
||||
# args: [--compile_commands_path=build]
|
||||
# pass_filenames: false
|
||||
|
||||
@@ -42,6 +42,10 @@ endif()
|
||||
|
||||
project(memgraph LANGUAGES C CXX)
|
||||
|
||||
# NOTE: once in a while this needs to be toggled to check headers are
|
||||
# correct and PCH isn't masking any include issues
|
||||
set(CMAKE_DISABLE_PRECOMPILE_HEADERS OFF)
|
||||
|
||||
#TODO: upgrade to cmake 3.24 + CheckIPOSupported
|
||||
#cmake_policy(SET CMP0138 NEW)
|
||||
#include(CheckIPOSupported)
|
||||
@@ -196,7 +200,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
|
||||
-Werror=switch -Werror=switch-bool -Werror=return-type \
|
||||
-Werror=return-stack-address \
|
||||
-Wno-c99-designator \
|
||||
-Wno-c99-designator -Wmissing-field-initializers \
|
||||
-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT")
|
||||
|
||||
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
|
||||
@@ -211,8 +215,8 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
# ** Static linking is allowed only for executables! **
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||
|
||||
# Use gold linker to speedup build
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
|
||||
# Use lld linker to speedup build
|
||||
add_link_options(-fuse-ld=lld) # TODO: use mold linker
|
||||
|
||||
# release flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
@@ -298,7 +302,7 @@ set(ENABLE_JEMALLOC ON)
|
||||
if (ASAN)
|
||||
message(WARNING "Disabling jemalloc as it doesn't work well with ASAN")
|
||||
set(ENABLE_JEMALLOC OFF)
|
||||
# Enable Addres sanitizer and get nicer stack traces in error messages.
|
||||
# Enable Address sanitizer and get nicer stack traces in error messages.
|
||||
# NOTE: AddressSanitizer uses llvm-symbolizer binary from the Clang
|
||||
# distribution to symbolize the stack traces (note that ideally the
|
||||
# llvm-symbolizer version must match the version of ASan runtime library).
|
||||
|
||||
@@ -175,7 +175,6 @@ license](./licenses/BSL.txt).</br> Memgraph Enterprise is available under the
|
||||
|
||||
- :purple_heart: [**Discord**](https://discord.gg/memgraph)
|
||||
- :ocean: [**Stack Overflow**](https://stackoverflow.com/questions/tagged/memgraphdb)
|
||||
- :busts_in_silhouette: [**Discourse forum**](https://discourse.memgraph.com/)
|
||||
- :bird: [**Twitter**](https://twitter.com/memgraphdb)
|
||||
- :movie_camera:
|
||||
[**YouTube**](https://www.youtube.com/channel/UCZ3HOJvHGxtQ_JHxOselBYg)
|
||||
|
||||
@@ -107,6 +107,10 @@ 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"
|
||||
|
||||
27
config/mappings.json
Normal file
27
config/mappings.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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,6 +255,16 @@ 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) {
|
||||
@@ -335,6 +345,12 @@ inline void map_insert(mgp_map *map, const char *key, mgp_value *value) {
|
||||
MgInvokeVoid(mgp_map_insert, map, key, value);
|
||||
}
|
||||
|
||||
inline void map_update(mgp_map *map, const char *key, mgp_value *value) {
|
||||
MgInvokeVoid(mgp_map_update, map, key, value);
|
||||
}
|
||||
|
||||
inline void map_erase(mgp_map *map, const char *key) { MgInvokeVoid(mgp_map_erase, map, key); }
|
||||
|
||||
inline size_t map_size(mgp_map *map) { return MgInvoke<size_t>(mgp_map_size, map); }
|
||||
|
||||
inline mgp_value *map_at(mgp_map *map, const char *key) { return MgInvoke<mgp_value *>(mgp_map_at, map, key); }
|
||||
@@ -361,6 +377,10 @@ 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);
|
||||
}
|
||||
@@ -383,6 +403,10 @@ inline bool vertex_has_label_named(mgp_vertex *v, const char *label_name) {
|
||||
|
||||
inline void vertex_add_label(mgp_vertex *vertex, mgp_label label) { MgInvokeVoid(mgp_vertex_add_label, vertex, label); }
|
||||
|
||||
inline void vertex_remove_label(mgp_vertex *vertex, mgp_label label) {
|
||||
MgInvokeVoid(mgp_vertex_remove_label, vertex, label);
|
||||
}
|
||||
|
||||
inline mgp_value *vertex_get_property(mgp_vertex *v, const char *property_name, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_value *>(mgp_vertex_get_property, v, property_name, memory);
|
||||
}
|
||||
@@ -391,6 +415,10 @@ 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);
|
||||
}
|
||||
@@ -427,6 +455,10 @@ 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);
|
||||
}
|
||||
@@ -712,7 +744,7 @@ inline void proc_add_deprecated_result(mgp_proc *proc, const char *name, mgp_typ
|
||||
MgInvokeVoid(mgp_proc_add_deprecated_result, proc, name, type);
|
||||
}
|
||||
|
||||
inline bool must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
|
||||
inline int must_abort(mgp_graph *graph) { return mgp_must_abort(graph); }
|
||||
|
||||
// mgp_result
|
||||
|
||||
@@ -750,4 +782,44 @@ inline void func_result_set_value(mgp_func_result *res, mgp_value *value, mgp_me
|
||||
MgInvokeVoid(mgp_func_result_set_value, res, value, memory);
|
||||
}
|
||||
|
||||
// Messages
|
||||
|
||||
inline mgp_source_type message_source_type(struct mgp_message *message) {
|
||||
return MgInvoke<mgp_source_type>(mgp_message_source_type, message);
|
||||
}
|
||||
|
||||
inline const char *message_payload(struct mgp_message *message) {
|
||||
return MgInvoke<const char *>(mgp_message_payload, message);
|
||||
}
|
||||
|
||||
inline size_t message_payload_size(struct mgp_message *message) {
|
||||
return MgInvoke<size_t>(mgp_message_payload_size, message);
|
||||
}
|
||||
|
||||
inline const char *message_topic_name(struct mgp_message *message) {
|
||||
return MgInvoke<const char *>(mgp_message_topic_name, message);
|
||||
}
|
||||
|
||||
inline const char *message_key(struct mgp_message *message) { return MgInvoke<const char *>(mgp_message_key, message); }
|
||||
|
||||
inline size_t message_key_size(struct mgp_message *message) { return MgInvoke<size_t>(mgp_message_key_size, message); }
|
||||
|
||||
inline int64_t message_timestamp(struct mgp_message *message) {
|
||||
return MgInvoke<int64_t>(mgp_message_timestamp, message);
|
||||
}
|
||||
|
||||
inline int64_t message_offset(struct mgp_message *message) { return MgInvoke<int64_t>(mgp_message_offset, message); }
|
||||
|
||||
inline size_t messages_size(struct mgp_messages *message) { return MgInvoke<size_t>(mgp_messages_size, message); }
|
||||
|
||||
inline mgp_message *messages_at(struct mgp_messages *message, size_t index) {
|
||||
return MgInvoke<mgp_message *>(mgp_messages_at, message, index);
|
||||
}
|
||||
|
||||
// Transformation
|
||||
|
||||
inline void module_add_transformation(struct mgp_module *module, const char *name, mgp_trans_cb cb) {
|
||||
return MgInvokeVoid(mgp_module_add_transformation, module, name, cb);
|
||||
}
|
||||
|
||||
} // namespace mgp
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -13,45 +13,96 @@
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace mg_exception {
|
||||
struct NotEnoughMemoryException : public std::exception {
|
||||
const char *what() const throw() { return "Not enough memory!"; }
|
||||
};
|
||||
|
||||
// Instead of writing this utility function, we could have used `fmt::format`, but that's not an ideal option here
|
||||
// because that would introduce dependency that would be propagated to the client code (if exceptions here would be
|
||||
// used). Since the functionality here is not complex + the code is not on a critical path, we opted for a pure C++
|
||||
// solution.
|
||||
template <typename FirstArg, typename... Args>
|
||||
std::string StringSerialize(FirstArg &&firstArg, Args &&...args) {
|
||||
std::stringstream stream;
|
||||
stream << firstArg;
|
||||
((stream << " " << args), ...);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
struct UnknownException : public std::exception {
|
||||
const char *what() const throw() { return "Unknown exception!"; }
|
||||
const char *what() const noexcept override { return "Unknown exception!"; }
|
||||
};
|
||||
|
||||
struct NotEnoughMemoryException : public std::exception {
|
||||
NotEnoughMemoryException()
|
||||
: message_{
|
||||
StringSerialize("Not enough memory! For more details please visit", "https://memgr.ph/memory-control")} {}
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
struct AllocationException : public std::exception {
|
||||
const char *what() const throw() { return "Could not allocate memory!"; }
|
||||
AllocationException()
|
||||
: message_{StringSerialize("Could not allocate memory. For more details please visit",
|
||||
"https://memgr.ph/memory-control")} {}
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
struct InsufficientBufferException : public std::exception {
|
||||
const char *what() const throw() { return "Buffer is not sufficient to process procedure!"; }
|
||||
const char *what() const noexcept override { return "Buffer is not sufficient to process procedure!"; }
|
||||
};
|
||||
|
||||
struct OutOfRangeException : public std::exception {
|
||||
const char *what() const throw() { return "Index out of range!"; }
|
||||
const char *what() const noexcept override { return "Index out of range!"; }
|
||||
};
|
||||
|
||||
struct LogicException : public std::exception {
|
||||
const char *what() const throw() { return "Logic exception, check the procedure signature!"; }
|
||||
const char *what() const noexcept override { return "Logic exception, check the procedure signature!"; }
|
||||
};
|
||||
|
||||
struct DeletedObjectException : public std::exception {
|
||||
const char *what() const throw() { return "Object is deleted!"; }
|
||||
const char *what() const noexcept override { return "Object is deleted!"; }
|
||||
};
|
||||
|
||||
struct InvalidArgumentException : public std::exception {
|
||||
const char *what() const throw() { return "Invalid argument!"; }
|
||||
const char *what() const noexcept override { return "Invalid argument!"; }
|
||||
};
|
||||
|
||||
struct InvalidIDException : public std::exception {
|
||||
const char *what() const throw() { return "Invalid ID!"; }
|
||||
InvalidIDException() : message_{"Invalid ID!"} {}
|
||||
explicit InvalidIDException(std::uint64_t identifier) : message_{StringSerialize("Invalid ID =", identifier)} {}
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
struct KeyAlreadyExistsException : public std::exception {
|
||||
const char *what() const throw() { return "Key you are trying to set already exists!"; }
|
||||
KeyAlreadyExistsException() : message_{"Key you are trying to set already exists!"} {}
|
||||
explicit KeyAlreadyExistsException(const std::string &key)
|
||||
: message_{StringSerialize("Key you are trying to set already exists! KEY = ", key)} {}
|
||||
const char *what() const noexcept override { return message_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
struct ImmutableObjectException : public std::exception {
|
||||
const char *what() const throw() { return "Object you are trying to change is immutable!"; }
|
||||
const char *what() const noexcept override { return "Object you are trying to change is immutable!"; }
|
||||
};
|
||||
|
||||
struct ValueConversionException : public std::exception {
|
||||
const char *what() const throw() { return "Error in value conversion!"; }
|
||||
const char *what() const noexcept override { return "Error in value conversion!"; }
|
||||
};
|
||||
|
||||
struct SerializationException : public std::exception {
|
||||
const char *what() const throw() { return "Error in serialization!"; }
|
||||
const char *what() const noexcept override { return "Error in serialization!"; }
|
||||
};
|
||||
|
||||
} // namespace mg_exception
|
||||
|
||||
@@ -462,6 +462,18 @@ void mgp_map_destroy(struct mgp_map *map);
|
||||
/// Return mgp_error::MGP_ERROR_KEY_ALREADY_EXISTS if a previous mapping already exists.
|
||||
enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, struct mgp_value *value);
|
||||
|
||||
/// Insert a mapping from a NULL terminated character string to a value.
|
||||
/// If a mapping with the same key already exists, it is replaced.
|
||||
/// In case of update, both the string and the value are copied into the map.
|
||||
/// Therefore, the map does not take ownership of the original key nor value, so
|
||||
/// you still need to free their memory explicitly.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate for insertion.
|
||||
enum mgp_error mgp_map_update(struct mgp_map *map, const char *key, struct mgp_value *value);
|
||||
|
||||
// Erase a mapping by key.
|
||||
// If the key doesn't exist in the map nothing happens
|
||||
enum mgp_error mgp_map_erase(struct mgp_map *map, const char *key);
|
||||
|
||||
/// Get the number of items stored in mgp_map.
|
||||
/// Current implementation always returns without errors.
|
||||
enum mgp_error mgp_map_size(struct mgp_map *map, size_t *result);
|
||||
@@ -635,6 +647,12 @@ 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
|
||||
@@ -652,6 +670,15 @@ 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.
|
||||
@@ -802,6 +829,15 @@ 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
|
||||
@@ -852,6 +888,22 @@ 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
|
||||
@@ -1448,7 +1500,10 @@ enum mgp_error mgp_log(enum mgp_log_level log_level, const char *output);
|
||||
/// @{
|
||||
|
||||
/// Return non-zero if the currently executing procedure should abort as soon as
|
||||
/// possible.
|
||||
/// possible. If non-zero the reasons are:
|
||||
/// (1) The transaction was requested to be terminated
|
||||
/// (2) The server is gracefully shutting down
|
||||
/// (3) The transaction has hit its timeout threshold
|
||||
///
|
||||
/// Procedures which perform heavyweight processing run the risk of running too
|
||||
/// long and going over the query execution time limit. To prevent this, such
|
||||
|
||||
1188
include/mgp.hpp
1188
include/mgp.hpp
File diff suppressed because it is too large
Load Diff
@@ -479,6 +479,12 @@ 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."""
|
||||
|
||||
4
init
4
init
@@ -93,6 +93,10 @@ setup_virtualenv tests/stress
|
||||
setup_virtualenv tests/integration/ldap
|
||||
|
||||
# Setup tests dependencies.
|
||||
# NOTE: This is commented out because of the build order (at the time of
|
||||
# execution mgclient is not built yet) which makes this setup to fail. mgclient
|
||||
# is built during the make phase. The tests/setup.sh is called under GHA CI
|
||||
# jobs.
|
||||
# cd tests
|
||||
# ./setup.sh
|
||||
# cd ..
|
||||
|
||||
@@ -261,3 +261,7 @@ import_external_library(librdtsc STATIC
|
||||
|
||||
# setup ctre
|
||||
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)
|
||||
|
||||
@@ -123,6 +123,7 @@ declare -A primary_urls=(
|
||||
["pulsar"]="http://$local_cache_host/git/pulsar.git"
|
||||
["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"
|
||||
)
|
||||
|
||||
# The goal of secondary urls is to have links to the "source of truth" of
|
||||
@@ -140,7 +141,7 @@ declare -A secondary_urls=(
|
||||
["rocksdb"]="https://github.com/facebook/rocksdb.git"
|
||||
["mgclient"]="https://github.com/memgraph/mgclient.git"
|
||||
["pymgclient"]="https://github.com/memgraph/pymgclient.git"
|
||||
["mgconsole"]="http://github.com/memgraph/mgconsole.git"
|
||||
["mgconsole"]="https://github.com/memgraph/mgconsole.git"
|
||||
["spdlog"]="https://github.com/gabime/spdlog"
|
||||
["nlohmann"]="https://raw.githubusercontent.com/nlohmann/json/4f8fba14066156b73f1189a2b8bd568bde5284c5/single_include/nlohmann/json.hpp"
|
||||
["neo4j"]="https://dist.neo4j.org/neo4j-community-5.6.0-unix.tar.gz"
|
||||
@@ -149,6 +150,7 @@ declare -A secondary_urls=(
|
||||
["pulsar"]="https://github.com/apache/pulsar.git"
|
||||
["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"
|
||||
)
|
||||
|
||||
# antlr
|
||||
@@ -210,7 +212,7 @@ pymgclient_tag="4f85c179e56302d46a1e3e2cf43509db65f062b3" # (2021-01-15)
|
||||
repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient]}" "pymgclient" "$pymgclient_tag"
|
||||
|
||||
# mgconsole
|
||||
mgconsole_tag="v1.3.0" # (2022-11-20)
|
||||
mgconsole_tag="v1.4.0" # (2023-05-21)
|
||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
|
||||
|
||||
spdlog_tag="v1.9.2" # (2021-08-12)
|
||||
@@ -246,3 +248,7 @@ mkdir -p ctre
|
||||
cd ctre
|
||||
file_get_try_double "${primary_urls[ctre]}" "${secondary_urls[ctre]}"
|
||||
cd ..
|
||||
|
||||
# abseil 20230125.3
|
||||
absl_ref="20230125.3"
|
||||
repo_clone_try_double "${primary_urls[absl]}" "${secondary_urls[absl]}" "absl" "$absl_ref"
|
||||
|
||||
@@ -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-20-07
|
||||
CHANGE DATE: 2027-13-09
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
202
licenses/third-party/abseil-cpp/LICENSE
vendored
Normal file
202
licenses/third-party/abseil-cpp/LICENSE
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
218
licenses/third-party/compile-time-regular-expressions/LICENSE
vendored
Normal file
218
licenses/third-party/compile-time-regular-expressions/LICENSE
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
@@ -6,6 +6,8 @@ project(memgraph_query_modules)
|
||||
|
||||
disallow_in_source_build()
|
||||
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
|
||||
# Everything that is installed here, should be under the "query_modules" component.
|
||||
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "query_modules")
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
@@ -37,9 +39,25 @@ endif()
|
||||
install(PROGRAMS $<TARGET_FILE:example_cpp>
|
||||
DESTINATION lib/memgraph/query_modules
|
||||
RENAME example_cpp.so)
|
||||
|
||||
# Also install the source of the example, so user can read it.
|
||||
install(FILES example.cpp DESTINATION lib/memgraph/query_modules/src)
|
||||
|
||||
add_library(example_cpp_transformation SHARED example_transformation.cpp)
|
||||
target_include_directories(example_cpp_transformation PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
target_compile_options(example_cpp_transformation PRIVATE -Wall)
|
||||
# Strip C++ transformation example in release build.
|
||||
if (lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET example_cpp_transformation POST_BUILD
|
||||
COMMAND strip -s $<TARGET_FILE:example_cpp_transformation>
|
||||
COMMENT "Stripping symbols and sections from the C++ transformation example module")
|
||||
endif()
|
||||
install(PROGRAMS $<TARGET_FILE:example_cpp_transformation>
|
||||
DESTINATION lib/memgraph/query_modules)
|
||||
|
||||
# Also install the source of the example, so user can read it.
|
||||
install(FILES example_cpp_transformation.cpp DESTINATION lib/memgraph/query_modules/src)
|
||||
|
||||
# Install the Python example and modules
|
||||
install(FILES example.py DESTINATION lib/memgraph/query_modules RENAME py_example.py)
|
||||
install(FILES graph_analyzer.py DESTINATION lib/memgraph/query_modules)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -18,7 +18,11 @@ void ProcImpl(std::vector<mgp::Value> arguments, mgp::Graph graph, mgp::RecordFa
|
||||
|
||||
void SampleReadProc(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
std::vector<mgp::Value> arguments;
|
||||
for (size_t i = 0; i < mgp::list_size(args); i++) {
|
||||
@@ -34,7 +38,11 @@ void SampleReadProc(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *resul
|
||||
}
|
||||
|
||||
void AddXNodes(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
auto graph = mgp::Graph(memgraph_graph);
|
||||
|
||||
std::vector<mgp::Value> arguments;
|
||||
@@ -49,7 +57,11 @@ void AddXNodes(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mg
|
||||
}
|
||||
|
||||
void Multiply(mgp_list *args, mgp_func_context *ctx, mgp_func_result *res, mgp_memory *memory) {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
std::vector<mgp::Value> arguments;
|
||||
for (size_t i = 0; i < mgp::list_size(args); i++) {
|
||||
@@ -67,7 +79,11 @@ void Multiply(mgp_list *args, mgp_func_context *ctx, mgp_func_result *res, mgp_m
|
||||
|
||||
extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
AddProcedure(SampleReadProc, "return_true", mgp::ProcedureType::Read,
|
||||
{mgp::Parameter("param_1", mgp::Type::Int), mgp::Parameter("param_2", mgp::Type::Double, 2.3)},
|
||||
@@ -77,7 +93,11 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem
|
||||
}
|
||||
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
mgp::AddProcedure(AddXNodes, "add_x_nodes", mgp::ProcedureType::Write, {mgp::Parameter("param_1", mgp::Type::Int)},
|
||||
{}, module, memory);
|
||||
@@ -87,7 +107,11 @@ extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *mem
|
||||
}
|
||||
|
||||
try {
|
||||
mgp::memory = memory;
|
||||
// The outcommented way of assigning the memory pointer is still
|
||||
// working, but it is deprecated because of certain concurrency
|
||||
// issues. Please use the guard instead.
|
||||
// mgp::memory = memory;
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
|
||||
mgp::AddFunction(Multiply, "multiply",
|
||||
{mgp::Parameter("int", mgp::Type::Int), mgp::Parameter("int", mgp::Type::Int, (int64_t)3)}, module,
|
||||
|
||||
64
query_modules/example_transformation.cpp
Normal file
64
query_modules/example_transformation.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 <exception>
|
||||
#include <mgp.hpp>
|
||||
|
||||
static constexpr std::string_view kQuery = "query";
|
||||
static constexpr std::string_view kParameters = "parameters";
|
||||
|
||||
std::string EscapeString(std::string s) {
|
||||
std::string sign = "'";
|
||||
std::string replace_sign;
|
||||
|
||||
size_t pos;
|
||||
while ((pos = s.find(sign)) != std::string::npos) {
|
||||
s.replace(pos, 1, replace_sign);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void Transformation(struct mgp_messages *messages, mgp_graph *graph, mgp_result *result, mgp_memory *memory) {
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
auto record_factory = mgp::RecordFactory(result);
|
||||
try {
|
||||
auto stream_messages = mgp::Messages(messages);
|
||||
|
||||
for (const mgp::Message &message : stream_messages) {
|
||||
auto record = record_factory.NewRecord();
|
||||
|
||||
auto payload = EscapeString(message.Payload());
|
||||
|
||||
auto query = "CREATE (:Data {payload: '" + payload + "'});";
|
||||
auto query_value = mgp::Value(query.data());
|
||||
|
||||
record.Insert(kQuery.data(), query_value);
|
||||
record.Insert(kParameters.data(), mgp::Value());
|
||||
}
|
||||
} catch (std::exception &ex) {
|
||||
record_factory.SetErrorMessage(ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int mgp_init_module(mgp_module *module, mgp_memory *memory) {
|
||||
try {
|
||||
mgp::MemoryDispatcherGuard guard(memory);
|
||||
mgp::AddTransformation(Transformation, "transform", module);
|
||||
} catch (const std::exception &e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int mgp_shutdown_module() { return 0; }
|
||||
@@ -14,7 +14,7 @@ set(CPACK_PACKAGE_VENDOR "Memgraph Ltd.")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
|
||||
"High performance, in-memory, transactional graph database")
|
||||
|
||||
# Setting arhitecture extension for deb packages
|
||||
# Setting architecture extension for deb packages
|
||||
set(MG_ARCH_EXTENSION_DEB "all")
|
||||
|
||||
if(${MG_ARCH} STREQUAL "x86_64")
|
||||
@@ -50,7 +50,7 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}
|
||||
# We also depend on `python3` because we embed it in Memgraph.
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0)")
|
||||
|
||||
# Setting arhitecture extension for rpm packages
|
||||
# Setting architecture extension for rpm packages
|
||||
set(MG_ARCH_EXTENSION_RPM "noarch")
|
||||
|
||||
if(${MG_ARCH} STREQUAL "x86_64")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/etc/memgraph/memgraph.conf
|
||||
/etc/memgraph/apoc_compatibility_mappings.json
|
||||
/etc/memgraph/auth_module/ldap.example.yaml
|
||||
/etc/logrotate.d/memgraph
|
||||
|
||||
@@ -132,6 +132,7 @@ 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,6 +18,7 @@ add_subdirectory(rpc)
|
||||
add_subdirectory(license)
|
||||
add_subdirectory(auth)
|
||||
add_subdirectory(audit)
|
||||
add_subdirectory(flags)
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
|
||||
@@ -33,13 +34,11 @@ set(mg_single_node_v2_sources
|
||||
memgraph.cpp
|
||||
)
|
||||
|
||||
set(mg_single_node_v2_libs stdc++fs Threads::Threads
|
||||
mg-telemetry mg-query mg-communication mg-memory mg-utils mg-auth mg-license mg-settings mg-glue mg-audit)
|
||||
|
||||
# memgraph main executable
|
||||
add_executable(memgraph ${mg_single_node_v2_sources})
|
||||
target_include_directories(memgraph PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(memgraph ${mg_single_node_v2_libs})
|
||||
target_link_libraries(memgraph stdc++fs Threads::Threads
|
||||
mg-telemetry mg-communication mg-memory mg-utils mg-license mg-settings mg-glue mg-flags)
|
||||
|
||||
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
|
||||
# should be dynamically exported, so that `dlopen` can correctly link the
|
||||
@@ -72,7 +71,7 @@ if(lower_build_type STREQUAL "release")
|
||||
COMMENT "Stripping symbols and sections from memgraph")
|
||||
endif()
|
||||
|
||||
# Generate the configuration file.
|
||||
# Generate the configuration file under the build directory.
|
||||
add_custom_command(TARGET memgraph POST_BUILD
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/config/generate.py
|
||||
${CMAKE_BINARY_DIR}/memgraph
|
||||
@@ -81,6 +80,11 @@ 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")
|
||||
@@ -116,6 +120,9 @@ 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -116,12 +116,12 @@ Log::~Log() {
|
||||
}
|
||||
|
||||
void Log::Record(const std::string &address, const std::string &username, const std::string &query,
|
||||
const storage::PropertyValue ¶ms) {
|
||||
const storage::PropertyValue ¶ms, const std::string &db) {
|
||||
if (!started_.load(std::memory_order_relaxed)) return;
|
||||
auto timestamp =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
buffer_->emplace(Item{timestamp, address, username, query, params});
|
||||
buffer_->emplace(Item{timestamp, address, username, query, params, db});
|
||||
}
|
||||
|
||||
void Log::ReopenLog() {
|
||||
@@ -136,8 +136,8 @@ void Log::Flush() {
|
||||
for (uint64_t i = 0; i < buffer_size_; ++i) {
|
||||
auto item = buffer_->pop();
|
||||
if (!item) break;
|
||||
log_.Write(fmt::format("{}.{:06d},{},{},{},{}\n", item->timestamp / 1000000, item->timestamp % 1000000,
|
||||
item->address, item->username, utils::Escape(item->query),
|
||||
log_.Write(fmt::format("{}.{:06d},{},{},{},{},{}\n", item->timestamp / 1000000, item->timestamp % 1000000,
|
||||
item->address, item->username, item->db, utils::Escape(item->query),
|
||||
utils::Escape(PropertyValueToJson(item->params).dump())));
|
||||
}
|
||||
log_.Sync();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -32,6 +32,7 @@ class Log {
|
||||
std::string username;
|
||||
std::string query;
|
||||
storage::PropertyValue params;
|
||||
std::string db;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -51,7 +52,7 @@ class Log {
|
||||
|
||||
/// Adds an entry to the audit log. Thread-safe.
|
||||
void Record(const std::string &address, const std::string &username, const std::string &query,
|
||||
const storage::PropertyValue ¶ms);
|
||||
const storage::PropertyValue ¶ms, const std::string &db);
|
||||
|
||||
/// Reopens the log file. Used for log file rotation. Thread-safe.
|
||||
void ReopenLog();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -314,4 +314,57 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name) {
|
||||
auto user = GetUser(name);
|
||||
if (user) {
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().GrantAll();
|
||||
} else {
|
||||
user->db_access().Add(db);
|
||||
}
|
||||
SaveUser(*user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name) {
|
||||
auto user = GetUser(name);
|
||||
if (user) {
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().DenyAll();
|
||||
} else {
|
||||
user->db_access().Remove(db);
|
||||
}
|
||||
SaveUser(*user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Auth::DeleteDatabase(const std::string &db) {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
auto user = GetUser(username);
|
||||
if (user) {
|
||||
user->db_access().Delete(db);
|
||||
SaveUser(*user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Auth::SetMainDatabase(const std::string &db, const std::string &name) {
|
||||
auto user = GetUser(name);
|
||||
if (user) {
|
||||
if (!user->db_access().SetDefault(db)) {
|
||||
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
||||
}
|
||||
SaveUser(*user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::auth
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -19,6 +19,9 @@
|
||||
#include "utils/settings.hpp"
|
||||
|
||||
namespace memgraph::auth {
|
||||
|
||||
static const constexpr char *const kAllDatabases = "*";
|
||||
|
||||
/**
|
||||
* This class serves as the main Authentication/Authorization storage.
|
||||
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
||||
@@ -155,6 +158,46 @@ class Auth final {
|
||||
*/
|
||||
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
* @brief Revoke access to individual database for a user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
bool RevokeDatabaseFromUser(const std::string &db, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Grant access to individual database for a user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
bool GrantDatabaseToUser(const std::string &db, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Delete a database from all users.
|
||||
*
|
||||
* @param db name of the database to delete
|
||||
* @throw AuthException if unable to read data
|
||||
*/
|
||||
void DeleteDatabase(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Set main database for an individual user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
bool SetMainDatabase(const std::string &db, const std::string &name);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
|
||||
// Auth is not thread-safe because modifying users and roles might require
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
|
||||
#include "auth/crypto.hpp"
|
||||
#include "auth/exceptions.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/constants.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "utils/cast.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/settings.hpp"
|
||||
@@ -35,18 +37,31 @@ namespace memgraph::auth {
|
||||
namespace {
|
||||
|
||||
// Constant list of all available permissions.
|
||||
const std::vector<Permission> kPermissionsAll = {Permission::MATCH, Permission::CREATE,
|
||||
Permission::MERGE, Permission::DELETE,
|
||||
Permission::SET, Permission::REMOVE,
|
||||
Permission::INDEX, Permission::STATS,
|
||||
Permission::CONSTRAINT, Permission::DUMP,
|
||||
Permission::AUTH, Permission::REPLICATION,
|
||||
Permission::DURABILITY, Permission::READ_FILE,
|
||||
Permission::FREE_MEMORY, Permission::TRIGGER,
|
||||
Permission::CONFIG, Permission::STREAM,
|
||||
Permission::MODULE_READ, Permission::MODULE_WRITE,
|
||||
Permission::WEBSOCKET, Permission::TRANSACTION_MANAGEMENT,
|
||||
Permission::STORAGE_MODE};
|
||||
const std::vector<Permission> kPermissionsAll = {Permission::MATCH,
|
||||
Permission::CREATE,
|
||||
Permission::MERGE,
|
||||
Permission::DELETE,
|
||||
Permission::SET,
|
||||
Permission::REMOVE,
|
||||
Permission::INDEX,
|
||||
Permission::STATS,
|
||||
Permission::CONSTRAINT,
|
||||
Permission::DUMP,
|
||||
Permission::AUTH,
|
||||
Permission::REPLICATION,
|
||||
Permission::DURABILITY,
|
||||
Permission::READ_FILE,
|
||||
Permission::FREE_MEMORY,
|
||||
Permission::TRIGGER,
|
||||
Permission::CONFIG,
|
||||
Permission::STREAM,
|
||||
Permission::MODULE_READ,
|
||||
Permission::MODULE_WRITE,
|
||||
Permission::WEBSOCKET,
|
||||
Permission::TRANSACTION_MANAGEMENT,
|
||||
Permission::STORAGE_MODE,
|
||||
Permission::MULTI_DATABASE_EDIT,
|
||||
Permission::MULTI_DATABASE_USE};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -98,6 +113,10 @@ std::string PermissionToString(Permission permission) {
|
||||
return "TRANSACTION_MANAGEMENT";
|
||||
case Permission::STORAGE_MODE:
|
||||
return "STORAGE_MODE";
|
||||
case Permission::MULTI_DATABASE_EDIT:
|
||||
return "MULTI_DATABASE_EDIT";
|
||||
case Permission::MULTI_DATABASE_USE:
|
||||
return "MULTI_DATABASE_USE";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,10 +463,11 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
if (!data["fine_grained_access_handler"].is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data["fine_grained_access_handler"].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
}
|
||||
auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
return {data["rolename"], permissions, std::move(fine_grained_access_handler)};
|
||||
}
|
||||
#endif
|
||||
@@ -464,6 +484,82 @@ bool operator==(const Role &first, const Role &second) {
|
||||
return first.rolename_ == second.rolename_ && first.permissions_ == second.permissions_;
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
void Databases::Add(const std::string &db) {
|
||||
if (allow_all_) {
|
||||
grants_dbs_.clear();
|
||||
allow_all_ = false;
|
||||
}
|
||||
grants_dbs_.emplace(db);
|
||||
denies_dbs_.erase(db);
|
||||
}
|
||||
|
||||
void Databases::Remove(const std::string &db) {
|
||||
denies_dbs_.emplace(db);
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
|
||||
void Databases::Delete(const std::string &db) {
|
||||
denies_dbs_.erase(db);
|
||||
if (!allow_all_) {
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
// Reset if default deleted
|
||||
if (default_db_ == db) {
|
||||
default_db_ = "";
|
||||
}
|
||||
}
|
||||
|
||||
void Databases::GrantAll() {
|
||||
allow_all_ = true;
|
||||
grants_dbs_.clear();
|
||||
denies_dbs_.clear();
|
||||
}
|
||||
|
||||
void Databases::DenyAll() {
|
||||
allow_all_ = false;
|
||||
grants_dbs_.clear();
|
||||
denies_dbs_.clear();
|
||||
}
|
||||
|
||||
bool Databases::SetDefault(const std::string &db) {
|
||||
if (!Contains(db)) return false;
|
||||
default_db_ = db;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Databases::Contains(const std::string &db) const {
|
||||
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
||||
}
|
||||
|
||||
const std::string &Databases::GetDefault() const {
|
||||
if (!Contains(default_db_)) {
|
||||
throw AuthException("No access to the set default database \"{}\".", default_db_);
|
||||
}
|
||||
return default_db_;
|
||||
}
|
||||
|
||||
nlohmann::json Databases::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["grants"] = grants_dbs_;
|
||||
data["denies"] = denies_dbs_;
|
||||
data["allow_all"] = allow_all_;
|
||||
data["default"] = default_db_;
|
||||
return data;
|
||||
}
|
||||
|
||||
Databases Databases::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load database data!");
|
||||
}
|
||||
if (!data["grants"].is_structured() || !data["denies"].is_structured() || !data["allow_all"].is_boolean() ||
|
||||
!data["default"].is_string()) {
|
||||
throw AuthException("Couldn't load database data!");
|
||||
}
|
||||
return {data["allow_all"], data["grants"], data["denies"], data["default"]};
|
||||
}
|
||||
#endif
|
||||
|
||||
User::User() {}
|
||||
|
||||
User::User(const std::string &username) : username_(utils::ToLowerCase(username)) {}
|
||||
@@ -472,11 +568,12 @@ User::User(const std::string &username, const std::string &password_hash, const
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler)
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
|
||||
: username_(utils::ToLowerCase(username)),
|
||||
password_hash_(password_hash),
|
||||
permissions_(permissions),
|
||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {}
|
||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
|
||||
database_access_(db_access) {}
|
||||
#endif
|
||||
|
||||
bool User::CheckPassword(const std::string &password) {
|
||||
@@ -576,8 +673,10 @@ nlohmann::json User::Serialize() const {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
|
||||
data["databases"] = database_access_.Serialize();
|
||||
} else {
|
||||
data["fine_grained_access_handler"] = {};
|
||||
data["databases"] = {};
|
||||
}
|
||||
#endif
|
||||
// The role shouldn't be serialized here, it is stored as a foreign key.
|
||||
@@ -594,11 +693,21 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
if (!data["fine_grained_access_handler"].is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
Databases db_access;
|
||||
if (data["databases"].is_structured()) {
|
||||
db_access = Databases::Deserialize(data["databases"]);
|
||||
} else {
|
||||
// Back-compatibility
|
||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||
db_access.Add(dbms::kDefaultDB);
|
||||
db_access.SetDefault(dbms::kDefaultDB);
|
||||
}
|
||||
auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
return {data["username"], data["password_hash"], permissions, fine_grained_access_handler};
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data["fine_grained_access_handler"].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
}
|
||||
return {data["username"], data["password_hash"], permissions, std::move(fine_grained_access_handler), db_access};
|
||||
}
|
||||
#endif
|
||||
return {data["username"], data["password_hash"], permissions};
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <json/json.hpp>
|
||||
#include "dbms/constants.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::auth {
|
||||
// These permissions must have values that are applicable for usage in a
|
||||
@@ -41,7 +44,9 @@ enum class Permission : uint64_t {
|
||||
MODULE_WRITE = 1U << 19U,
|
||||
WEBSOCKET = 1U << 20U,
|
||||
TRANSACTION_MANAGEMENT = 1U << 21U,
|
||||
STORAGE_MODE = 1U << 22U
|
||||
STORAGE_MODE = 1U << 22U,
|
||||
MULTI_DATABASE_EDIT = 1U << 23U,
|
||||
MULTI_DATABASE_USE = 1U << 24U,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -237,6 +242,85 @@ class Role final {
|
||||
|
||||
bool operator==(const Role &first, const Role &second);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
class Databases final {
|
||||
public:
|
||||
Databases() : grants_dbs_({dbms::kDefaultDB}), allow_all_(false), default_db_(dbms::kDefaultDB) {}
|
||||
|
||||
Databases(const Databases &) = default;
|
||||
Databases &operator=(const Databases &) = default;
|
||||
Databases(Databases &&) noexcept = default;
|
||||
Databases &operator=(Databases &&) noexcept = default;
|
||||
~Databases() = default;
|
||||
|
||||
/**
|
||||
* @brief Add database to the list of granted access. @note allow_all_ will be false after execution
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Add(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Remove database to the list of granted access.
|
||||
* @note if allow_all_ is set, the flag will remain set and the
|
||||
* database will be added to the set of denied databases.
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Remove(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
|
||||
* @note allow_all_ is not changed
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Delete(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to true and clears grants and denied sets.
|
||||
*/
|
||||
void GrantAll();
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||
*/
|
||||
void DenyAll();
|
||||
|
||||
/**
|
||||
* @brief Set the default database.
|
||||
*/
|
||||
bool SetDefault(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Checks if access is grated to the database.
|
||||
*
|
||||
* @param db name of the database
|
||||
* @return true if allow_all and not denied or granted
|
||||
*/
|
||||
bool Contains(const std::string &db) const;
|
||||
|
||||
bool GetAllowAll() const { return allow_all_; }
|
||||
const std::set<std::string> &GetGrants() const { return grants_dbs_; }
|
||||
const std::set<std::string> &GetDenies() const { return denies_dbs_; }
|
||||
const std::string &GetDefault() const;
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
static Databases Deserialize(const nlohmann::json &data);
|
||||
|
||||
private:
|
||||
Databases(bool allow_all, std::set<std::string> grant, std::set<std::string> deny,
|
||||
const std::string &default_db = dbms::kDefaultDB)
|
||||
: grants_dbs_(grant), denies_dbs_(deny), allow_all_(allow_all), default_db_(default_db) {}
|
||||
|
||||
std::set<std::string> grants_dbs_; //!< set of databases with granted access
|
||||
std::set<std::string> denies_dbs_; //!< set of databases with denied access
|
||||
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
|
||||
std::string default_db_; //!< user's default database
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO (mferencevic): Implement password expiry.
|
||||
class User final {
|
||||
public:
|
||||
@@ -246,7 +330,7 @@ class User final {
|
||||
User(const std::string &username, const std::string &password_hash, const Permissions &permissions);
|
||||
#ifdef MG_ENTERPRISE
|
||||
User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler);
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
|
||||
#endif
|
||||
User(const User &) = default;
|
||||
User &operator=(const User &) = default;
|
||||
@@ -279,6 +363,11 @@ class User final {
|
||||
|
||||
const Role *role() const;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
Databases &db_access() { return database_access_; }
|
||||
const Databases &db_access() const { return database_access_; }
|
||||
#endif
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
@@ -292,6 +381,7 @@ class User final {
|
||||
Permissions permissions_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||
Databases database_access_;
|
||||
#endif
|
||||
std::optional<Role> role_;
|
||||
};
|
||||
|
||||
@@ -22,3 +22,4 @@ target_link_libraries(mg-communication Boost::headers Threads::Threads mg-utils
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
|
||||
target_include_directories(mg-communication SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
target_precompile_headers(mg-communication INTERFACE http/server.hpp <boost/beast/websocket.hpp> bolt/v1/session.hpp)
|
||||
|
||||
@@ -19,7 +19,7 @@ inline constexpr uint8_t kPreamble[4] = {0x60, 0x60, 0xB0, 0x17};
|
||||
|
||||
enum class Signature : uint8_t {
|
||||
Noop = 0x00,
|
||||
Init = 0x01,
|
||||
Init = 0x01, // v3+ now HELLO
|
||||
LogOn = 0x6A,
|
||||
LogOff = 0x6B,
|
||||
AckFailure = 0x0E, // only v1
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
@@ -24,8 +26,12 @@
|
||||
#include "communication/bolt/v1/states/executing.hpp"
|
||||
#include "communication/bolt/v1/states/handshake.hpp"
|
||||
#include "communication/bolt/v1/states/init.hpp"
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
namespace memgraph::communication::bolt {
|
||||
|
||||
@@ -48,14 +54,26 @@ 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>>;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Session object
|
||||
*
|
||||
* @param input_stream stream to read from
|
||||
* @param output_stream stream to write to
|
||||
* @param impl a default high-level implementation to use (has to be defined)
|
||||
*/
|
||||
Session(TInputStream *input_stream, TOutputStream *output_stream)
|
||||
: input_stream_(*input_stream), output_stream_(*output_stream) {}
|
||||
: input_stream_(*input_stream), output_stream_(*output_stream), session_uuid_(utils::GenerateUUID()) {}
|
||||
|
||||
virtual ~Session() {}
|
||||
virtual ~Session() = default;
|
||||
|
||||
Session(const Session &) = delete;
|
||||
Session &operator=(const Session &) = delete;
|
||||
Session(Session &&) noexcept = delete;
|
||||
Session &operator=(Session &&) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Process the given `query` with `params`.
|
||||
@@ -64,7 +82,9 @@ class Session {
|
||||
*/
|
||||
virtual std::pair<std::vector<std::string>, std::optional<int>> Interpret(
|
||||
const std::string &query, const std::map<std::string, Value> ¶ms,
|
||||
const std::map<std::string, memgraph::communication::bolt::Value> &metadata) = 0;
|
||||
const std::map<std::string, memgraph::communication::bolt::Value> &extra) = 0;
|
||||
|
||||
virtual void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) = 0;
|
||||
|
||||
/**
|
||||
* Put results of the processed query in the `encoder`.
|
||||
@@ -86,7 +106,7 @@ class Session {
|
||||
*/
|
||||
virtual std::map<std::string, Value> Discard(std::optional<int> n, std::optional<int> qid) = 0;
|
||||
|
||||
virtual void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &) = 0;
|
||||
virtual void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> ¶ms) = 0;
|
||||
virtual void CommitTransaction() = 0;
|
||||
virtual void RollbackTransaction() = 0;
|
||||
|
||||
@@ -99,7 +119,6 @@ class Session {
|
||||
/** Return the name of the server that should be used for the Bolt INIT
|
||||
* message. */
|
||||
virtual std::optional<std::string> GetServerNameForInit() = 0;
|
||||
|
||||
/**
|
||||
* Executes the session after data has been read into the buffer.
|
||||
* Goes through the bolt states in order to execute commands from the client.
|
||||
@@ -140,6 +159,7 @@ class Session {
|
||||
break;
|
||||
case State::Idle:
|
||||
case State::Result:
|
||||
at_least_one_run_ = true;
|
||||
state_ = StateExecutingRun(*this, state_);
|
||||
break;
|
||||
case State::Error:
|
||||
@@ -161,8 +181,13 @@ class Session {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rethink if there is a way to hide some members. At the momement all
|
||||
// of them are public.
|
||||
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_;
|
||||
|
||||
@@ -174,6 +199,7 @@ class Session {
|
||||
|
||||
bool handshake_done_{false};
|
||||
State state_{State::Handshake};
|
||||
bool at_least_one_run_{false};
|
||||
|
||||
struct Version {
|
||||
uint8_t major;
|
||||
@@ -182,6 +208,9 @@ class Session {
|
||||
|
||||
Version version_;
|
||||
|
||||
std::string GetDatabaseName() const override = 0;
|
||||
std::string UUID() const final { return session_uuid_; }
|
||||
|
||||
private:
|
||||
void ClientFailureInvalidData() {
|
||||
// Set the state to Close.
|
||||
@@ -197,6 +226,8 @@ class Session {
|
||||
// of the session to trigger session cleanup and socket close.
|
||||
throw SessionException("Something went wrong during session execution!");
|
||||
}
|
||||
|
||||
const std::string session_uuid_; //!< unique identifier of the session (auto generated)
|
||||
};
|
||||
|
||||
} // namespace memgraph::communication::bolt
|
||||
|
||||
@@ -152,6 +152,7 @@ State StateExecutingRun(TSession &session, State state) {
|
||||
return RunHandlerV4<TSession>(signature, session, state, marker);
|
||||
}
|
||||
case 5:
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::BoltMessages);
|
||||
return RunHandlerV5<TSession>(signature, session, state, marker);
|
||||
default:
|
||||
spdlog::trace("Unsupported bolt version:{}.{})!", session.version_.major, session.version_.minor);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -73,23 +74,6 @@ inline std::pair<std::string, std::string> ExceptionToErrorMessage(const std::ex
|
||||
"should be in database logs."};
|
||||
}
|
||||
|
||||
namespace helpers {
|
||||
|
||||
/** Extracts metadata from the extras field.
|
||||
* NOTE: In order to avoid a copy, the metadata in moved.
|
||||
* TODO: Update if extra field is used for anything else.
|
||||
*/
|
||||
inline std::map<std::string, Value> ConsumeMetadata(Value &extra) {
|
||||
std::map<std::string, Value> md;
|
||||
auto &md_tv = extra.ValueMap()["tx_metadata"];
|
||||
if (md_tv.IsMap()) {
|
||||
md = std::move(md_tv.ValueMap());
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
|
||||
namespace details {
|
||||
|
||||
template <bool is_pull, typename TSession>
|
||||
@@ -224,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] '{}'", query.ValueString());
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
@@ -282,12 +266,17 @@ State HandleRunV4(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] '{}'", query.ValueString());
|
||||
try {
|
||||
session.Configure(extra.ValueMap());
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
|
||||
spdlog::debug("[Run - {}] '{}'", session.GetDatabaseName(), query.ValueString());
|
||||
|
||||
try {
|
||||
// Interpret can throw.
|
||||
const auto [header, qid] =
|
||||
session.Interpret(query.ValueString(), params.ValueMap(), helpers::ConsumeMetadata(extra));
|
||||
const auto [header, qid] = session.Interpret(query.ValueString(), params.ValueMap(), extra.ValueMap());
|
||||
// Convert std::string to Value
|
||||
std::vector<Value> vec;
|
||||
std::map<std::string, Value> data;
|
||||
@@ -399,7 +388,8 @@ State HandleBegin(TSession &session, const State state, const Marker marker) {
|
||||
}
|
||||
|
||||
try {
|
||||
session.BeginTransaction(helpers::ConsumeMetadata(extra));
|
||||
session.Configure(extra.ValueMap());
|
||||
session.BeginTransaction(extra.ValueMap());
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
@@ -507,7 +497,7 @@ State HandleRoute(TSession &session, const Marker marker) {
|
||||
|
||||
template <typename TSession>
|
||||
State HandleLogOff() {
|
||||
// Not arguments sent, the user just needs to reauthenticate
|
||||
// No arguments sent, the user just needs to reauthenticate
|
||||
return State::Init;
|
||||
}
|
||||
} // namespace memgraph::communication::bolt
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "communication/bolt/v1/state.hpp"
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
#include "communication/exceptions.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
@@ -173,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", *server_name});
|
||||
metadata.insert({"server", std::move(*server_name)});
|
||||
}
|
||||
bool success_sent = session.encoder_.MessageSuccess(metadata);
|
||||
if (!success_sent) {
|
||||
@@ -248,8 +249,9 @@ State StateInitRunV5(TSession &session, Marker marker, Signature signature) {
|
||||
}
|
||||
// Stay in Init
|
||||
return State::Init;
|
||||
}
|
||||
|
||||
} else if (signature == Signature::LogOn) {
|
||||
if (signature == Signature::LogOn) {
|
||||
if (marker != Marker::TinyStruct1) [[unlikely]] {
|
||||
spdlog::trace("Expected TinyStruct1 marker, but received 0x{:02X}!", utils::UnderlyingCast(marker));
|
||||
spdlog::trace(
|
||||
@@ -273,11 +275,10 @@ State StateInitRunV5(TSession &session, Marker marker, Signature signature) {
|
||||
return State::Close;
|
||||
}
|
||||
return State::Idle;
|
||||
|
||||
} else [[unlikely]] {
|
||||
spdlog::trace("Expected Init signature, but received 0x{:02X}!", utils::UnderlyingCast(signature));
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
spdlog::trace("Expected Init signature, but received 0x{:02X}!", utils::UnderlyingCast(signature));
|
||||
return State::Close;
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ struct Path {
|
||||
for (uint i = 0; i < edges.size(); i++) {
|
||||
const auto &e = edges[i];
|
||||
const auto &v = vertices[i + 1];
|
||||
UnboundedEdge unbounded_edge{e.id, e.type, e.properties};
|
||||
UnboundedEdge unbounded_edge{e.id, e.type, e.properties, e.element_id};
|
||||
add_element(this->edges, unbounded_edge, e.to == v.id ? 1 : -1, 1);
|
||||
add_element(this->vertices, v, 1, 0);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -43,6 +43,7 @@ class Buffer final {
|
||||
Buffer(Buffer &&) = delete;
|
||||
Buffer &operator=(const Buffer &) = delete;
|
||||
Buffer &operator=(Buffer &&) = delete;
|
||||
~Buffer() = default;
|
||||
|
||||
/**
|
||||
* This class provides all functions from the buffer that are needed to allow
|
||||
@@ -50,12 +51,13 @@ class Buffer final {
|
||||
*/
|
||||
class ReadEnd {
|
||||
public:
|
||||
ReadEnd(Buffer *buffer);
|
||||
explicit ReadEnd(Buffer *buffer);
|
||||
|
||||
ReadEnd(const ReadEnd &) = delete;
|
||||
ReadEnd(ReadEnd &&) = delete;
|
||||
ReadEnd &operator=(const ReadEnd &) = delete;
|
||||
ReadEnd &operator=(ReadEnd &&) = delete;
|
||||
~ReadEnd() = default;
|
||||
|
||||
uint8_t *data();
|
||||
|
||||
@@ -77,12 +79,13 @@ class Buffer final {
|
||||
*/
|
||||
class WriteEnd {
|
||||
public:
|
||||
WriteEnd(Buffer *buffer);
|
||||
explicit WriteEnd(Buffer *buffer);
|
||||
|
||||
WriteEnd(const WriteEnd &) = delete;
|
||||
WriteEnd(WriteEnd &&) = delete;
|
||||
WriteEnd &operator=(const WriteEnd &) = delete;
|
||||
WriteEnd &operator=(WriteEnd &&) = delete;
|
||||
~WriteEnd() = default;
|
||||
|
||||
io::network::StreamBuffer Allocate();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -15,6 +15,10 @@
|
||||
#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 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -17,6 +17,10 @@
|
||||
#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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
|
||||
namespace memgraph::communication::http {
|
||||
|
||||
template <class TRequestHandler, typename TSessionData>
|
||||
class Listener final : public std::enable_shared_from_this<Listener<TRequestHandler, TSessionData>> {
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
class Listener final : public std::enable_shared_from_this<Listener<TRequestHandler, TSessionContext>> {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
using SessionHandler = Session<TRequestHandler, TSessionData>;
|
||||
using std::enable_shared_from_this<Listener<TRequestHandler, TSessionData>>::shared_from_this;
|
||||
using SessionHandler = Session<TRequestHandler, TSessionContext>;
|
||||
using std::enable_shared_from_this<Listener<TRequestHandler, TSessionContext>>::shared_from_this;
|
||||
|
||||
public:
|
||||
Listener(const Listener &) = delete;
|
||||
@@ -50,8 +50,9 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
|
||||
tcp::endpoint GetEndpoint() const { return acceptor_.local_endpoint(); }
|
||||
|
||||
private:
|
||||
Listener(boost::asio::io_context &ioc, TSessionData *data, ServerContext *context, tcp::endpoint endpoint)
|
||||
: ioc_(ioc), data_(data), context_(context), acceptor_(ioc) {
|
||||
Listener(boost::asio::io_context &ioc, TSessionContext *session_context, ServerContext *context,
|
||||
tcp::endpoint endpoint)
|
||||
: ioc_(ioc), session_context_(session_context), context_(context), acceptor_(ioc) {
|
||||
boost::beast::error_code ec;
|
||||
|
||||
// Open the acceptor
|
||||
@@ -95,13 +96,13 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
|
||||
return LogError(ec, "accept");
|
||||
}
|
||||
|
||||
SessionHandler::Create(std::move(socket), data_, *context_)->Run();
|
||||
SessionHandler::Create(std::move(socket), session_context_, *context_)->Run();
|
||||
|
||||
DoAccept();
|
||||
}
|
||||
|
||||
boost::asio::io_context &ioc_;
|
||||
TSessionData *data_;
|
||||
TSessionContext *session_context_;
|
||||
ServerContext *context_;
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
@@ -21,14 +21,12 @@
|
||||
|
||||
namespace memgraph::communication::http {
|
||||
|
||||
template <class TRequestHandler, typename TSessionData>
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
class Server final {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
public:
|
||||
explicit Server(io::network::Endpoint endpoint, TSessionData *data, ServerContext *context)
|
||||
: listener_{Listener<TRequestHandler, TSessionData>::Create(
|
||||
ioc_, data, context, tcp::endpoint{boost::asio::ip::make_address(endpoint.address), endpoint.port})} {}
|
||||
explicit Server(io::network::Endpoint endpoint, TSessionContext *session_context, ServerContext *context);
|
||||
|
||||
Server(const Server &) = delete;
|
||||
Server(Server &&) = delete;
|
||||
@@ -40,11 +38,7 @@ class Server final {
|
||||
"Server wasn't shutdown properly");
|
||||
}
|
||||
|
||||
void Start() {
|
||||
MG_ASSERT(!background_thread_, "The server was already started!");
|
||||
listener_->Run();
|
||||
background_thread_.emplace([this] { ioc_.run(); });
|
||||
}
|
||||
void Start();
|
||||
|
||||
void Shutdown() { ioc_.stop(); }
|
||||
|
||||
@@ -54,12 +48,30 @@ class Server final {
|
||||
}
|
||||
}
|
||||
bool IsRunning() const { return background_thread_ && !ioc_.stopped(); }
|
||||
tcp::endpoint GetEndpoint() const { return listener_->GetEndpoint(); }
|
||||
tcp::endpoint GetEndpoint() const;
|
||||
|
||||
private:
|
||||
boost::asio::io_context ioc_;
|
||||
|
||||
std::shared_ptr<Listener<TRequestHandler, TSessionData>> listener_;
|
||||
std::shared_ptr<Listener<TRequestHandler, TSessionContext>> listener_;
|
||||
std::optional<std::thread> background_thread_;
|
||||
};
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
Server<TRequestHandler, TSessionContext>::Server(io::network::Endpoint endpoint, TSessionContext *session_context,
|
||||
ServerContext *context)
|
||||
: listener_{Listener<TRequestHandler, TSessionContext>::Create(
|
||||
ioc_, session_context, context,
|
||||
tcp::endpoint{boost::asio::ip::make_address(endpoint.address), endpoint.port})} {}
|
||||
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
void Server<TRequestHandler, TSessionContext>::Start() {
|
||||
MG_ASSERT(!background_thread_, "The server was already started!");
|
||||
listener_->Run();
|
||||
background_thread_.emplace([this] { ioc_.run(); });
|
||||
}
|
||||
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
boost::asio::ip::tcp::endpoint Server<TRequestHandler, TSessionContext>::GetEndpoint() const {
|
||||
return listener_->GetEndpoint();
|
||||
}
|
||||
} // namespace memgraph::communication::http
|
||||
|
||||
@@ -42,10 +42,10 @@ inline void LogError(boost::beast::error_code ec, const std::string_view what) {
|
||||
spdlog::warn("HTTP session failed on {}: {}", what, ec.message());
|
||||
}
|
||||
|
||||
template <class TRequestHandler, typename TSessionData>
|
||||
class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSessionData>> {
|
||||
template <class TRequestHandler, typename TSessionContext>
|
||||
class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSessionContext>> {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
using std::enable_shared_from_this<Session<TRequestHandler, TSessionData>>::shared_from_this;
|
||||
using std::enable_shared_from_this<Session<TRequestHandler, TSessionContext>>::shared_from_this;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
@@ -72,7 +72,7 @@ class Session : public std::enable_shared_from_this<Session<TRequestHandler, TSe
|
||||
using PlainSocket = boost::beast::tcp_stream;
|
||||
using SSLSocket = boost::beast::ssl_stream<boost::beast::tcp_stream>;
|
||||
|
||||
explicit Session(tcp::socket &&socket, TSessionData *data, ServerContext &context)
|
||||
explicit Session(tcp::socket &&socket, TSessionContext *data, ServerContext &context)
|
||||
: stream_(CreateSocket(std::move(socket), context)),
|
||||
handler_(data),
|
||||
strand_{boost::asio::make_strand(GetExecutor())} {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -39,7 +39,7 @@ namespace memgraph::communication {
|
||||
* second, checks all sessions for expiration and shuts them down if they have
|
||||
* expired.
|
||||
*/
|
||||
template <class TSession, class TSessionData>
|
||||
template <class TSession, class TSessionContext>
|
||||
class Listener final {
|
||||
private:
|
||||
// The maximum number of events handled per execution thread is 1. This is
|
||||
@@ -48,10 +48,10 @@ class Listener final {
|
||||
// can take a long time.
|
||||
static const int kMaxEvents = 1;
|
||||
|
||||
using SessionHandler = Session<TSession, TSessionData>;
|
||||
using SessionHandler = Session<TSession, TSessionContext>;
|
||||
|
||||
public:
|
||||
Listener(TSessionData *data, ServerContext *context, int inactivity_timeout_sec, const std::string &service_name,
|
||||
Listener(TSessionContext *data, ServerContext *context, int inactivity_timeout_sec, const std::string &service_name,
|
||||
size_t workers_count)
|
||||
: data_(data),
|
||||
alive_(false),
|
||||
@@ -259,7 +259,7 @@ class Listener final {
|
||||
|
||||
io::network::Epoll epoll_;
|
||||
|
||||
TSessionData *data_;
|
||||
TSessionContext *data_;
|
||||
|
||||
utils::SpinLock lock_;
|
||||
std::vector<std::unique_ptr<SessionHandler>> sessions_;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -46,10 +46,10 @@ namespace memgraph::communication {
|
||||
* @tparam TSession the server can handle different Sessions, each session
|
||||
* represents a different protocol so the same network infrastructure
|
||||
* can be used for handling different protocols
|
||||
* @tparam TSessionData the class with objects that will be forwarded to the
|
||||
* @tparam TSessionContext the class with objects that will be forwarded to the
|
||||
* session
|
||||
*/
|
||||
template <typename TSession, typename TSessionData>
|
||||
template <typename TSession, typename TSessionContext>
|
||||
class Server final {
|
||||
public:
|
||||
using Socket = io::network::Socket;
|
||||
@@ -58,12 +58,12 @@ class Server final {
|
||||
* Constructs and binds server to endpoint, operates on session data and
|
||||
* invokes workers_count workers
|
||||
*/
|
||||
Server(const io::network::Endpoint &endpoint, TSessionData *session_data, ServerContext *context,
|
||||
Server(io::network::Endpoint endpoint, TSessionContext *session_context, ServerContext *context,
|
||||
int inactivity_timeout_sec, const std::string &service_name,
|
||||
size_t workers_count = std::thread::hardware_concurrency())
|
||||
: alive_(false),
|
||||
endpoint_(endpoint),
|
||||
listener_(session_data, context, inactivity_timeout_sec, service_name, workers_count),
|
||||
endpoint_(std::move(endpoint)),
|
||||
listener_(session_context, context, inactivity_timeout_sec, service_name, workers_count),
|
||||
service_name_(service_name) {}
|
||||
|
||||
~Server() {
|
||||
@@ -156,7 +156,7 @@ class Server final {
|
||||
|
||||
Socket socket_;
|
||||
io::network::Endpoint endpoint_;
|
||||
Listener<TSession, TSessionData> listener_;
|
||||
Listener<TSession, TSessionContext> listener_;
|
||||
|
||||
const std::string service_name_;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -22,6 +22,10 @@
|
||||
#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"
|
||||
@@ -69,10 +73,10 @@ class OutputStream final {
|
||||
* sessions. It handles socket ownership, inactivity timeout and protocol
|
||||
* wrapping.
|
||||
*/
|
||||
template <class TSession, class TSessionData>
|
||||
template <class TSession, class TSessionContext>
|
||||
class Session final {
|
||||
public:
|
||||
Session(io::network::Socket &&socket, TSessionData *data, ServerContext *context, int inactivity_timeout_sec)
|
||||
Session(io::network::Socket &&socket, TSessionContext *data, ServerContext *context, int inactivity_timeout_sec)
|
||||
: socket_(std::move(socket)),
|
||||
output_stream_([this](const uint8_t *data, size_t len, bool have_more) { return Write(data, len, have_more); }),
|
||||
session_(data, socket_.endpoint(), input_buffer_.read_end(), &output_stream_),
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
|
||||
namespace memgraph::communication::v2 {
|
||||
|
||||
template <class TSession, class TSessionData>
|
||||
class Listener final : public std::enable_shared_from_this<Listener<TSession, TSessionData>> {
|
||||
template <class TSession, class TSessionContext>
|
||||
class Listener final : public std::enable_shared_from_this<Listener<TSession, TSessionContext>> {
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
using SessionHandler = Session<TSession, TSessionData>;
|
||||
using std::enable_shared_from_this<Listener<TSession, TSessionData>>::shared_from_this;
|
||||
using SessionHandler = Session<TSession, TSessionContext>;
|
||||
using std::enable_shared_from_this<Listener<TSession, TSessionContext>>::shared_from_this;
|
||||
|
||||
public:
|
||||
Listener(const Listener &) = delete;
|
||||
@@ -59,10 +59,10 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
|
||||
bool IsRunning() const noexcept { return alive_.load(std::memory_order_relaxed); }
|
||||
|
||||
private:
|
||||
Listener(boost::asio::io_context &io_context, TSessionData *data, ServerContext *server_context,
|
||||
Listener(boost::asio::io_context &io_context, TSessionContext *session_context, ServerContext *server_context,
|
||||
tcp::endpoint &endpoint, const std::string_view service_name, const uint64_t inactivity_timeout_sec)
|
||||
: io_context_(io_context),
|
||||
data_(data),
|
||||
session_context_(session_context),
|
||||
server_context_(server_context),
|
||||
acceptor_(io_context_),
|
||||
endpoint_{endpoint},
|
||||
@@ -111,8 +111,8 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
|
||||
return OnError(ec, "accept");
|
||||
}
|
||||
|
||||
auto session = SessionHandler::Create(std::move(socket), data_, *server_context_, endpoint_, inactivity_timeout_,
|
||||
service_name_);
|
||||
auto session = SessionHandler::Create(std::move(socket), session_context_, *server_context_, endpoint_,
|
||||
inactivity_timeout_, service_name_);
|
||||
session->Start();
|
||||
DoAccept();
|
||||
}
|
||||
@@ -123,7 +123,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TSession, TS
|
||||
}
|
||||
|
||||
boost::asio::io_context &io_context_;
|
||||
TSessionData *data_;
|
||||
TSessionContext *session_context_;
|
||||
ServerContext *server_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -60,53 +60,32 @@ using ServerEndpoint = boost::asio::ip::tcp::endpoint;
|
||||
* @tparam TSession the server can handle different Sessions, each session
|
||||
* represents a different protocol so the same network infrastructure
|
||||
* can be used for handling different protocols
|
||||
* @tparam TSessionData the class with objects that will be forwarded to the
|
||||
* @tparam TSessionContext the class with objects that will be forwarded to the
|
||||
* session
|
||||
*/
|
||||
template <typename TSession, typename TSessionData>
|
||||
template <typename TSession, typename TSessionContext>
|
||||
class Server final {
|
||||
using ServerHandler = Server<TSession, TSessionData>;
|
||||
using ServerHandler = Server<TSession, TSessionContext>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs and binds server to endpoint, operates on session data and
|
||||
* invokes workers_count workers
|
||||
*/
|
||||
Server(ServerEndpoint &endpoint, TSessionData *session_data, ServerContext *server_context,
|
||||
const int inactivity_timeout_sec, const std::string_view service_name,
|
||||
size_t workers_count = std::thread::hardware_concurrency())
|
||||
: endpoint_{endpoint},
|
||||
service_name_{service_name},
|
||||
context_thread_pool_{workers_count},
|
||||
listener_{Listener<TSession, TSessionData>::Create(context_thread_pool_.GetIOContext(), session_data,
|
||||
server_context, endpoint_, service_name_,
|
||||
inactivity_timeout_sec)} {}
|
||||
Server(ServerEndpoint &endpoint, TSessionContext *session_context, ServerContext *server_context,
|
||||
int inactivity_timeout_sec, std::string_view service_name,
|
||||
size_t workers_count = std::thread::hardware_concurrency());
|
||||
|
||||
~Server() { MG_ASSERT(!IsRunning(), "Server wasn't shutdown properly"); }
|
||||
~Server();
|
||||
|
||||
Server(const Server &) = delete;
|
||||
Server(Server &&) = delete;
|
||||
Server &operator=(const Server &) = delete;
|
||||
Server &operator=(Server &&) = delete;
|
||||
|
||||
const auto &Endpoint() const {
|
||||
MG_ASSERT(IsRunning(), "You can't get the server endpoint when it's not running!");
|
||||
return endpoint_;
|
||||
}
|
||||
const auto &Endpoint() const;
|
||||
|
||||
bool Start() {
|
||||
if (IsRunning()) {
|
||||
spdlog::error("The server is already running");
|
||||
return false;
|
||||
}
|
||||
listener_->Start();
|
||||
|
||||
spdlog::info("{} server is fully armed and operational", service_name_);
|
||||
spdlog::info("{} listening on {}", service_name_, endpoint_.address());
|
||||
context_thread_pool_.Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Start();
|
||||
|
||||
void Shutdown() {
|
||||
context_thread_pool_.Shutdown();
|
||||
@@ -115,14 +94,56 @@ class Server final {
|
||||
|
||||
void AwaitShutdown() { context_thread_pool_.AwaitShutdown(); }
|
||||
|
||||
bool IsRunning() const noexcept { return context_thread_pool_.IsRunning() && listener_->IsRunning(); }
|
||||
bool IsRunning() const noexcept;
|
||||
|
||||
private:
|
||||
ServerEndpoint endpoint_;
|
||||
std::string service_name_;
|
||||
|
||||
IOContextThreadPool context_thread_pool_;
|
||||
std::shared_ptr<Listener<TSession, TSessionData>> listener_;
|
||||
std::shared_ptr<Listener<TSession, TSessionContext>> listener_;
|
||||
};
|
||||
|
||||
template <typename TSession, typename TSessionContext>
|
||||
Server<TSession, TSessionContext>::~Server() {
|
||||
MG_ASSERT(!IsRunning(), "Server wasn't shutdown properly");
|
||||
}
|
||||
|
||||
template <typename TSession, typename TSessionContext>
|
||||
Server<TSession, TSessionContext>::Server(ServerEndpoint &endpoint, TSessionContext *session_context,
|
||||
ServerContext *server_context, const int inactivity_timeout_sec,
|
||||
const std::string_view service_name, size_t workers_count)
|
||||
: endpoint_{endpoint},
|
||||
service_name_{service_name},
|
||||
context_thread_pool_{workers_count},
|
||||
listener_{Listener<TSession, TSessionContext>::Create(context_thread_pool_.GetIOContext(), session_context,
|
||||
server_context, endpoint_, service_name_,
|
||||
inactivity_timeout_sec)} {}
|
||||
|
||||
template <typename TSession, typename TSessionContext>
|
||||
bool Server<TSession, TSessionContext>::Start() {
|
||||
if (IsRunning()) {
|
||||
spdlog::error("The server is already running");
|
||||
return false;
|
||||
}
|
||||
listener_->Start();
|
||||
|
||||
spdlog::info("{} server is fully armed and operational", service_name_);
|
||||
spdlog::info("{} listening on {}", service_name_, endpoint_.address());
|
||||
context_thread_pool_.Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TSession, typename TSessionContext>
|
||||
const auto &Server<TSession, TSessionContext>::Endpoint() const {
|
||||
MG_ASSERT(IsRunning(), "You can't get the server endpoint when it's not running!");
|
||||
return endpoint_;
|
||||
}
|
||||
|
||||
template <typename TSession, typename TSessionContext>
|
||||
bool Server<TSession, TSessionContext>::IsRunning() const noexcept {
|
||||
return context_thread_pool_.IsRunning() && listener_->IsRunning();
|
||||
}
|
||||
|
||||
} // namespace memgraph::communication::v2
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
@@ -41,9 +43,11 @@
|
||||
#include <boost/beast/websocket/rfc6455.hpp>
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
|
||||
#include "communication/bolt/v1/session.hpp"
|
||||
#include "communication/buffer.hpp"
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/exceptions.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
@@ -95,10 +99,10 @@ class OutputStream final {
|
||||
* Websocket Sessions. It handles socket ownership, inactivity timeout and protocol
|
||||
* wrapping.
|
||||
*/
|
||||
template <typename TSession, typename TSessionData>
|
||||
class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>> {
|
||||
template <typename TSession, typename TSessionContext>
|
||||
class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TSession, TSessionContext>> {
|
||||
using WebSocket = boost::beast::websocket::stream<boost::beast::tcp_stream>;
|
||||
using std::enable_shared_from_this<WebsocketSession<TSession, TSessionData>>::shared_from_this;
|
||||
using std::enable_shared_from_this<WebsocketSession<TSession, TSessionContext>>::shared_from_this;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
@@ -106,6 +110,17 @@ 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;
|
||||
WebsocketSession(WebsocketSession &&) noexcept = delete;
|
||||
WebsocketSession &operator=(WebsocketSession &&) noexcept = delete;
|
||||
|
||||
// Start the asynchronous accept operation
|
||||
template <class Body, class Allocator>
|
||||
void DoAccept(boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>> req) {
|
||||
@@ -151,15 +166,20 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
|
||||
private:
|
||||
// Take ownership of the socket
|
||||
explicit WebsocketSession(tcp::socket &&socket, TSessionData *data, tcp::endpoint endpoint,
|
||||
explicit WebsocketSession(tcp::socket &&socket, TSessionContext *session_context, tcp::endpoint endpoint,
|
||||
std::string_view service_name)
|
||||
: 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_(data, endpoint, input_buffer_.read_end(), &output_stream_),
|
||||
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} {}
|
||||
service_name_{service_name} {
|
||||
#ifdef MG_ENTERPRISE
|
||||
session_context_->Register(session_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnAccept(boost::beast::error_code ec) {
|
||||
if (ec) {
|
||||
@@ -242,6 +262,7 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
communication::Buffer input_buffer_;
|
||||
OutputStream output_stream_;
|
||||
TSession session_;
|
||||
TSessionContext *session_context_;
|
||||
tcp::endpoint endpoint_;
|
||||
tcp::endpoint remote_endpoint_;
|
||||
std::string_view service_name_;
|
||||
@@ -253,11 +274,11 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
* Sessions. It handles socket ownership, inactivity timeout and protocol
|
||||
* wrapping.
|
||||
*/
|
||||
template <typename TSession, typename TSessionData>
|
||||
class Session final : public std::enable_shared_from_this<Session<TSession, TSessionData>> {
|
||||
template <typename TSession, typename TSessionContext>
|
||||
class Session final : public std::enable_shared_from_this<Session<TSession, TSessionContext>> {
|
||||
using TCPSocket = tcp::socket;
|
||||
using SSLSocket = boost::asio::ssl::stream<TCPSocket>;
|
||||
using std::enable_shared_from_this<Session<TSession, TSessionData>>::shared_from_this;
|
||||
using std::enable_shared_from_this<Session<TSession, TSessionContext>>::shared_from_this;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
@@ -265,11 +286,16 @@ 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;
|
||||
Session &operator=(const Session &) = delete;
|
||||
Session &operator=(Session &&) = delete;
|
||||
~Session() = default;
|
||||
|
||||
bool Start() {
|
||||
if (execution_active_) {
|
||||
@@ -334,18 +360,23 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Session(tcp::socket &&socket, TSessionData *data, ServerContext &server_context, tcp::endpoint endpoint,
|
||||
const std::chrono::seconds inactivity_timeout_sec, std::string_view service_name)
|
||||
explicit Session(tcp::socket &&socket, TSessionContext *session_context, ServerContext &server_context,
|
||||
tcp::endpoint endpoint, const std::chrono::seconds inactivity_timeout_sec,
|
||||
std::string_view service_name)
|
||||
: 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_(data, endpoint, input_buffer_.read_end(), &output_stream_),
|
||||
data_{data},
|
||||
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
|
||||
@@ -382,6 +413,8 @@ 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);
|
||||
@@ -396,7 +429,8 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
spdlog::info("Switching {} to websocket connection", remote_endpoint_);
|
||||
if (std::holds_alternative<TCPSocket>(socket_)) {
|
||||
auto sock = std::get<TCPSocket>(std::move(socket_));
|
||||
WebsocketSession<TSession, TSessionData>::Create(std::move(sock), data_, endpoint_, service_name_)
|
||||
WebsocketSession<TSession, TSessionContext>::Create(std::move(sock), session_context_, endpoint_,
|
||||
service_name_)
|
||||
->DoAccept(parser.release());
|
||||
execution_active_ = false;
|
||||
return;
|
||||
@@ -535,7 +569,7 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
communication::Buffer input_buffer_;
|
||||
OutputStream output_stream_;
|
||||
TSession session_;
|
||||
TSessionData *data_;
|
||||
TSessionContext *session_context_;
|
||||
tcp::endpoint endpoint_;
|
||||
tcp::endpoint remote_endpoint_;
|
||||
std::string_view service_name_;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -31,7 +31,7 @@
|
||||
template <typename TElement>
|
||||
class RingBuffer {
|
||||
public:
|
||||
explicit RingBuffer(int capacity) : capacity_(capacity) { buffer_ = std::make_unique<TElement[]>(capacity_); }
|
||||
explicit RingBuffer(int capacity) : capacity_(capacity), buffer_{std::make_unique<TElement[]>(capacity_)} {}
|
||||
|
||||
RingBuffer(const RingBuffer &) = delete;
|
||||
RingBuffer(RingBuffer &&) = delete;
|
||||
|
||||
18
src/dbms/constants.hpp
Normal file
18
src/dbms/constants.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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::dbms {
|
||||
|
||||
constexpr static const char *kDefaultDB = "memgraph"; //!< Name of the default database
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
110
src/dbms/global.hpp
Normal file
110
src/dbms/global.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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 <concepts>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
enum class DeleteError : uint8_t {
|
||||
DEFAULT_DB,
|
||||
USING,
|
||||
NON_EXISTENT,
|
||||
FAIL,
|
||||
DISK_FAIL,
|
||||
};
|
||||
|
||||
enum class NewError : uint8_t {
|
||||
NO_CONFIGS,
|
||||
EXISTS,
|
||||
DEFUNCT,
|
||||
GENERIC,
|
||||
};
|
||||
|
||||
enum class SetForResult : uint8_t {
|
||||
SUCCESS,
|
||||
ALREADY_SET,
|
||||
FAIL,
|
||||
};
|
||||
|
||||
/**
|
||||
* UnknownSession Exception
|
||||
*
|
||||
* Used to indicate that an unknown session was used.
|
||||
*/
|
||||
class UnknownSessionException : public utils::BasicException {
|
||||
public:
|
||||
using utils::BasicException::BasicException;
|
||||
};
|
||||
|
||||
/**
|
||||
* UnknownDatabase Exception
|
||||
*
|
||||
* Used to indicate that an unknown database was used.
|
||||
*/
|
||||
class UnknownDatabaseException : public utils::BasicException {
|
||||
public:
|
||||
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
|
||||
142
src/dbms/handler.hpp
Normal file
142
src/dbms/handler.hpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// 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 <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "global.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/sync_ptr.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
/**
|
||||
* @brief Generic multi-database content handler.
|
||||
*
|
||||
* @tparam TContext
|
||||
* @tparam TConfig
|
||||
*/
|
||||
template <typename TContext, typename TConfig>
|
||||
class Handler {
|
||||
public:
|
||||
using NewResult = utils::BasicResult<NewError, std::shared_ptr<TContext>>;
|
||||
|
||||
/**
|
||||
* @brief Empty Handler constructor.
|
||||
*
|
||||
*/
|
||||
Handler() {}
|
||||
|
||||
/**
|
||||
* @brief Generate a new context and corresponding configuration.
|
||||
*
|
||||
* @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... 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<std::shared_ptr<TContext>>
|
||||
*/
|
||||
std::optional<std::shared_ptr<TContext>> Get(const std::string &name) {
|
||||
if (auto search = items_.find(name); search != items_.end()) {
|
||||
return search->second.get();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the config.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
bool Delete(const std::string &name) {
|
||||
if (auto itr = items_.find(name); itr != items_.end()) {
|
||||
itr->second.DestroyAndSync();
|
||||
items_.erase(itr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a name is already used.
|
||||
*
|
||||
* @param name Name to check
|
||||
* @return true if a context/config pair is already associated with the name
|
||||
*/
|
||||
bool Has(const std::string &name) const { return items_.find(name) != items_.end(); }
|
||||
|
||||
auto begin() { return items_.begin(); }
|
||||
auto end() { return items_.end(); }
|
||||
auto begin() const { return items_.begin(); }
|
||||
auto end() const { return items_.end(); }
|
||||
auto cbegin() const { return items_.cbegin(); }
|
||||
auto cend() const { return items_.cend(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @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
|
||||
9
src/flags/CMakeLists.txt
Normal file
9
src/flags/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
add_library(mg-flags STATIC audit.cpp
|
||||
bolt.cpp
|
||||
general.cpp
|
||||
isolation_level.cpp
|
||||
log_level.cpp
|
||||
memory_limit.cpp
|
||||
run_time_configurable.cpp)
|
||||
target_include_directories(mg-flags PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-flags PUBLIC spdlog::spdlog mg-settings mg-utils)
|
||||
19
src/flags/all.hpp
Normal file
19
src/flags/all.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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 "flags/audit.hpp"
|
||||
#include "flags/bolt.hpp"
|
||||
#include "flags/general.hpp"
|
||||
#include "flags/isolation_level.hpp"
|
||||
#include "flags/log_level.hpp"
|
||||
#include "flags/memory_limit.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
28
src/flags/audit.cpp
Normal file
28
src/flags/audit.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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/audit.hpp"
|
||||
|
||||
#include "audit/log.hpp"
|
||||
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
// Audit logging flags.
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(audit_enabled, false, "Set to true to enable audit logging.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(audit_buffer_size, memgraph::audit::kBufferSizeDefault,
|
||||
"Maximum number of items in the audit log buffer.", FLAG_IN_RANGE(1, INT32_MAX));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(audit_buffer_flush_interval_ms, memgraph::audit::kBufferFlushIntervalMillisDefault,
|
||||
"Interval (in milliseconds) used for flushing the audit log buffer.",
|
||||
FLAG_IN_RANGE(10, INT32_MAX));
|
||||
#endif
|
||||
23
src/flags/audit.hpp
Normal file
23
src/flags/audit.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 "gflags/gflags.h"
|
||||
|
||||
// Audit logging flags.
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(audit_enabled);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(audit_buffer_size);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(audit_buffer_flush_interval_ms);
|
||||
#endif
|
||||
42
src/flags/bolt.cpp
Normal file
42
src/flags/bolt.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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/bolt.hpp"
|
||||
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
|
||||
// Bolt server flags.
|
||||
DEFINE_string(bolt_address, "0.0.0.0", "IP address on which the Bolt server should listen.");
|
||||
|
||||
DEFINE_VALIDATED_int32(bolt_port, 7687, "Port on which the Bolt server should listen.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(bolt_num_workers, std::max(std::thread::hardware_concurrency(), 1U),
|
||||
"Number of workers used by the Bolt server. By default, this will be the "
|
||||
"number of processing units available on the machine.",
|
||||
FLAG_IN_RANGE(1, INT32_MAX));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(bolt_session_inactivity_timeout, 1800,
|
||||
"Time in seconds after which inactive Bolt sessions will be "
|
||||
"closed.",
|
||||
FLAG_IN_RANGE(1, INT32_MAX));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
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.");
|
||||
29
src/flags/bolt.hpp
Normal file
29
src/flags/bolt.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 "gflags/gflags.h"
|
||||
|
||||
// Bolt server flags.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(bolt_address);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(bolt_port);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(bolt_num_workers);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(bolt_session_inactivity_timeout);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
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);
|
||||
198
src/flags/general.cpp
Normal file
198
src/flags/general.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
// 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 "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>
|
||||
|
||||
// Short help flag.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(h, false, "Print usage and exit.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(monitoring_address, "0.0.0.0",
|
||||
"IP address on which the websocket server for Memgraph monitoring should listen.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(metrics_address, "0.0.0.0",
|
||||
"IP address on which the Memgraph server for exposing metrics should listen.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(monitoring_port, 7444,
|
||||
"Port on which the websocket server for Memgraph monitoring should listen.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(metrics_port, 9091, "Port on which the Memgraph server for exposing metrics should listen.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(init_file, "",
|
||||
"Path to cypherl file that is used for configuring users and database schema before server starts.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(init_data_file, "", "Path to cypherl file that is used for creating data after server starts.");
|
||||
|
||||
// General purpose flags.
|
||||
// NOTE: The `data_directory` flag must be the same here and in
|
||||
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(data_directory, "mg_data", "Path to directory in which to save all permanent data.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(data_recovery_on_startup, false, "Controls whether the database recovers persisted data on startup.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(memory_warning_threshold, 1024,
|
||||
"Memory warning threshold, in MB. If Memgraph detects there is "
|
||||
"less available RAM it will log a warning. Set to 0 to "
|
||||
"disable.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(allow_load_csv, true, "Controls whether LOAD CSV clause is allowed in queries.");
|
||||
|
||||
// Storage flags.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_uint64(storage_gc_cycle_sec, 30, "Storage garbage collector interval (in seconds).",
|
||||
FLAG_IN_RANGE(1, 24 * 3600));
|
||||
// NOTE: The `storage_properties_on_edges` flag must be the same here and in
|
||||
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_properties_on_edges, false, "Controls whether edges have properties.");
|
||||
|
||||
// storage_recover_on_startup deprecated; use data_recovery_on_startup instead
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(storage_recover_on_startup, false,
|
||||
"Controls whether the storage recovers persisted data on startup.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_uint64(storage_snapshot_interval_sec, 0,
|
||||
"Storage snapshot creation interval (in seconds). Set "
|
||||
"to 0 to disable periodic snapshot creation.",
|
||||
FLAG_IN_RANGE(0, 7 * 24 * 3600));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_wal_enabled, false,
|
||||
"Controls whether the storage uses write-ahead-logging. To enable "
|
||||
"WAL periodic snapshots must be enabled.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_uint64(storage_snapshot_retention_count, 3, "The number of snapshots that should always be kept.",
|
||||
FLAG_IN_RANGE(1, 1000000));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_uint64(storage_wal_file_size_kib, memgraph::storage::Config::Durability().wal_file_size_kibibytes,
|
||||
"Minimum file size of each WAL file.",
|
||||
FLAG_IN_RANGE(1, static_cast<unsigned long>(1000) * 1024));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_uint64(storage_wal_file_flush_every_n_tx,
|
||||
memgraph::storage::Config::Durability().wal_file_flush_every_n_tx,
|
||||
"Issue a 'fsync' call after this amount of transactions are written to the "
|
||||
"WAL file. Set to 1 for fully synchronous operation.",
|
||||
FLAG_IN_RANGE(1, 1000000));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_snapshot_on_exit, false, "Controls whether the storage creates another snapshot on exit.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(storage_items_per_batch, memgraph::storage::Config::Durability().items_per_batch,
|
||||
"The number of edges and vertices stored in a batch in a snapshot file.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_parallel_index_recovery, false,
|
||||
"Controls whether the index creation can be done in a multithreaded fashion.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(storage_recovery_thread_count,
|
||||
std::max(static_cast<uint64_t>(std::thread::hardware_concurrency()),
|
||||
memgraph::storage::Config::Durability().recovery_thread_count),
|
||||
"The number of threads used to recover persisted data from disk.");
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_delete_on_drop, true,
|
||||
"If set to true the query 'DROP DATABASE x' will delete the underlying storage as well.");
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(telemetry_enabled, false,
|
||||
"Set to true to enable telemetry. We collect information about the "
|
||||
"running system (CPU and memory information) and information about "
|
||||
"the database runtime (vertex and edge counts and resource usage) "
|
||||
"to allow for easier improvement of the product.");
|
||||
|
||||
// Streams flags
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(
|
||||
stream_transaction_conflict_retries, 30,
|
||||
"Number of times to retry when a stream transformation fails to commit because of conflicting transactions");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(
|
||||
stream_transaction_retry_interval, 500,
|
||||
"Retry interval in milliseconds when a stream transformation fails to commit because of conflicting transactions");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(kafka_bootstrap_servers, "",
|
||||
"List of default Kafka brokers as a comma separated list of broker host or host:port.");
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Pulsar brokers.");
|
||||
|
||||
// Query flags.
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_double(query_execution_timeout_sec, -1,
|
||||
"Maximum allowed query execution time. Queries exceeding this "
|
||||
"limit will be aborted. Value of 0 means no limit.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
|
||||
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
|
||||
"The MAIN instance allocates a new thread for each REPLICA.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(replication_restore_state_on_startup, false, "Restore replication state on startup, e.g. recover replica");
|
||||
|
||||
DEFINE_VALIDATED_string(query_modules_directory, "",
|
||||
"Directory where modules with custom query procedures are stored. "
|
||||
"NOTE: Multiple comma-separated directories can be defined.",
|
||||
{
|
||||
if (value.empty()) return true;
|
||||
const auto directories = memgraph::utils::Split(value, ",");
|
||||
for (const auto &dir : directories) {
|
||||
if (!memgraph::utils::DirExists(dir)) {
|
||||
std::cout << "Expected --" << flagname << " to point to directories." << std::endl;
|
||||
std::cout << dir << " is not a directory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
auto memgraph::flags::ParseQueryModulesDirectory() -> std::vector<std::filesystem::path> {
|
||||
const auto directories = memgraph::utils::Split(FLAGS_query_modules_directory, ",");
|
||||
std::vector<std::filesystem::path> query_modules_directories;
|
||||
query_modules_directories.reserve(directories.size());
|
||||
std::transform(directories.begin(), directories.end(), std::back_inserter(query_modules_directories),
|
||||
[](const auto &dir) { return dir; });
|
||||
|
||||
return query_modules_directories;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(query_callable_mappings_path, "",
|
||||
"The path to mappings that describes aliases to callables in cypher queries in the form of key-value "
|
||||
"pairs in a json file. With this option query module procedures that do not exist in memgraph can be "
|
||||
"mapped to ones that exist.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_string(license_key, "", "License key for Memgraph Enterprise.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_string(organization_name, "", "Organization name.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(auth_user_or_role_name_regex, memgraph::glue::kDefaultUserRoleRegex.data(),
|
||||
"Set to the regular expression that each user or role name must fulfill.");
|
||||
122
src/flags/general.hpp
Normal file
122
src/flags/general.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// 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 "gflags/gflags.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
// Short help flag.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(h);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(monitoring_address);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(monitoring_port);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(metrics_address);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(metrics_port);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(init_file);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(init_data_file);
|
||||
|
||||
// General purpose flags.
|
||||
// NOTE: The `data_directory` flag must be the same here and in
|
||||
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(data_directory);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(data_recovery_on_startup);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(memory_warning_threshold);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(allow_load_csv);
|
||||
|
||||
// Storage flags.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_gc_cycle_sec);
|
||||
// NOTE: The `storage_properties_on_edges` flag must be the same here and in
|
||||
// `mg_import_csv`. If you change it, make sure to change it there as well.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_properties_on_edges);
|
||||
// storage_recover_on_startup deprecated; use data_recovery_on_startup instead
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_recover_on_startup);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_snapshot_interval_sec);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_wal_enabled);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_snapshot_retention_count);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_wal_file_size_kib);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_wal_file_flush_every_n_tx);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_snapshot_on_exit);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_items_per_batch);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_parallel_index_recovery);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(storage_recovery_thread_count);
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_delete_on_drop);
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(telemetry_enabled);
|
||||
|
||||
// Streams flags
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint32(stream_transaction_conflict_retries);
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint32(stream_transaction_retry_interval);
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(kafka_bootstrap_servers);
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(pulsar_service_url);
|
||||
|
||||
// Query flags.
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_double(query_execution_timeout_sec);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(query_modules_directory);
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(query_callable_mappings_path);
|
||||
namespace memgraph::flags {
|
||||
auto ParseQueryModulesDirectory() -> std::vector<std::filesystem::path>;
|
||||
} // namespace memgraph::flags
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(replication_replica_check_frequency_sec);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(replication_restore_state_on_startup);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(license_key);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(organization_name);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(auth_user_or_role_name_regex);
|
||||
54
src/flags/isolation_level.cpp
Normal file
54
src/flags/isolation_level.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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/isolation_level.hpp"
|
||||
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
inline constexpr std::array isolation_level_mappings{
|
||||
std::pair{std::string_view{"SNAPSHOT_ISOLATION"}, memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION},
|
||||
std::pair{std::string_view{"READ_COMMITTED"}, memgraph::storage::IsolationLevel::READ_COMMITTED},
|
||||
std::pair{std::string_view{"READ_UNCOMMITTED"}, memgraph::storage::IsolationLevel::READ_UNCOMMITTED}};
|
||||
|
||||
const std::string isolation_level_help_string =
|
||||
fmt::format("Default isolation level used for the transactions. Allowed values: {}",
|
||||
memgraph::utils::GetAllowedEnumValuesString(isolation_level_mappings));
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_string(isolation_level, "SNAPSHOT_ISOLATION", isolation_level_help_string.c_str(), {
|
||||
if (const auto result = memgraph::utils::IsValidEnumValueString(value, isolation_level_mappings); result.HasError()) {
|
||||
switch (result.GetError()) {
|
||||
case memgraph::utils::ValidationError::EmptyValue: {
|
||||
std::cout << "Isolation level cannot be empty." << std::endl;
|
||||
break;
|
||||
}
|
||||
case memgraph::utils::ValidationError::InvalidValue: {
|
||||
std::cout << "Invalid value for isolation level. Allowed values: "
|
||||
<< memgraph::utils::GetAllowedEnumValuesString(isolation_level_mappings) << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
memgraph::storage::IsolationLevel memgraph::flags::ParseIsolationLevel() {
|
||||
const auto isolation_level =
|
||||
memgraph::utils::StringToEnum<memgraph::storage::IsolationLevel>(FLAGS_isolation_level, isolation_level_mappings);
|
||||
MG_ASSERT(isolation_level, "Invalid isolation level");
|
||||
return *isolation_level;
|
||||
}
|
||||
19
src/flags/isolation_level.hpp
Normal file
19
src/flags/isolation_level.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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/isolation_level.hpp"
|
||||
|
||||
namespace memgraph::flags {
|
||||
|
||||
memgraph::storage::IsolationLevel ParseIsolationLevel();
|
||||
|
||||
} // namespace memgraph::flags
|
||||
122
src/flags/log_level.cpp
Normal file
122
src/flags/log_level.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// 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/log_level.hpp"
|
||||
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
// Logging flags
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(also_log_to_stderr, false, "Log messages go to stderr in addition to logfiles");
|
||||
DEFINE_string(log_file, "", "Path to where the log should be stored.");
|
||||
|
||||
inline constexpr std::array log_level_mappings{
|
||||
std::pair{"TRACE"sv, spdlog::level::trace}, std::pair{"DEBUG"sv, spdlog::level::debug},
|
||||
std::pair{"INFO"sv, spdlog::level::info}, std::pair{"WARNING"sv, spdlog::level::warn},
|
||||
std::pair{"ERROR"sv, spdlog::level::err}, std::pair{"CRITICAL"sv, spdlog::level::critical}};
|
||||
|
||||
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) {
|
||||
if (const auto result = memgraph::utils::IsValidEnumValueString(value, log_level_mappings); result.HasError()) {
|
||||
const auto error = result.GetError();
|
||||
switch (error) {
|
||||
case memgraph::utils::ValidationError::EmptyValue: {
|
||||
std::cout << "Log level cannot be empty." << std::endl;
|
||||
break;
|
||||
}
|
||||
case memgraph::utils::ValidationError::InvalidValue: {
|
||||
std::cout << "Invalid value for log level. Allowed values: "
|
||||
<< memgraph::utils::GetAllowedEnumValuesString(log_level_mappings) << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
MG_ASSERT(log_level, "Invalid log level");
|
||||
return *log_level;
|
||||
}
|
||||
|
||||
// 5 weeks * 7 days
|
||||
inline constexpr auto log_retention_count = 35;
|
||||
void CreateLoggerFromSink(const auto &sinks, const auto log_level) {
|
||||
auto logger = std::make_shared<spdlog::logger>("memgraph_log", sinks.begin(), sinks.end());
|
||||
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_log_file.empty()) {
|
||||
// get local time
|
||||
time_t current_time{0};
|
||||
struct tm *local_time{nullptr};
|
||||
|
||||
time(¤t_time);
|
||||
local_time = localtime(¤t_time);
|
||||
|
||||
sinks.emplace_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(
|
||||
FLAGS_log_file, local_time->tm_hour, local_time->tm_min, false, log_retention_count));
|
||||
}
|
||||
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);
|
||||
}
|
||||
29
src/flags/log_level.hpp
Normal file
29
src/flags/log_level.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 <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
|
||||
41
src/flags/memory_limit.cpp
Normal file
41
src/flags/memory_limit.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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/memory_limit.hpp"
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(
|
||||
memory_limit, 0,
|
||||
"Total memory limit in MiB. Set to 0 to use the default values which are 100\% of the phyisical memory if the swap "
|
||||
"is enabled and 90\% of the physical memory otherwise.");
|
||||
|
||||
int64_t memgraph::flags::GetMemoryLimit() {
|
||||
if (FLAGS_memory_limit == 0) {
|
||||
auto maybe_total_memory = memgraph::utils::sysinfo::TotalMemory();
|
||||
MG_ASSERT(maybe_total_memory, "Failed to fetch the total physical memory");
|
||||
const auto maybe_swap_memory = memgraph::utils::sysinfo::SwapTotalMemory();
|
||||
MG_ASSERT(maybe_swap_memory, "Failed to fetch the total swap memory");
|
||||
|
||||
if (*maybe_swap_memory == 0) {
|
||||
// take only 90% of the total memory
|
||||
*maybe_total_memory *= 9;
|
||||
*maybe_total_memory /= 10;
|
||||
}
|
||||
return *maybe_total_memory * 1024;
|
||||
}
|
||||
|
||||
// We parse the memory as MiB every time
|
||||
return FLAGS_memory_limit * 1024 * 1024;
|
||||
}
|
||||
17
src/flags/memory_limit.hpp
Normal file
17
src/flags/memory_limit.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 <cstdint>
|
||||
|
||||
namespace memgraph::flags {
|
||||
int64_t GetMemoryLimit();
|
||||
} // namespace memgraph::flags
|
||||
111
src/flags/run_time_configurable.cpp
Normal file
111
src/flags/run_time_configurable.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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
|
||||
26
src/flags/run_time_configurable.hpp
Normal file
26
src/flags/run_time_configurable.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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,4 +1,4 @@
|
||||
set(mg_glue_sources auth.cpp auth_checker.cpp auth_handler.cpp communication.cpp)
|
||||
|
||||
add_library(mg-glue STATIC ${mg_glue_sources})
|
||||
target_link_libraries(mg-glue mg-query mg-auth)
|
||||
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)
|
||||
target_link_libraries(mg-glue mg-query mg-auth mg-audit)
|
||||
target_precompile_headers(mg-glue INTERFACE auth_checker.hpp auth_handler.hpp)
|
||||
|
||||
14
src/glue/MonitoringServerT.cpp
Normal file
14
src/glue/MonitoringServerT.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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/MonitoringServerT.hpp"
|
||||
|
||||
template class memgraph::communication::http::Server<
|
||||
memgraph::http::MetricsRequestHandler<memgraph::dbms::SessionContext>, memgraph::dbms::SessionContext>;
|
||||
25
src/glue/MonitoringServerT.hpp
Normal file
25
src/glue/MonitoringServerT.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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 "communication/http/server.hpp"
|
||||
#include "dbms/session_context.hpp"
|
||||
#include "http_handlers/metrics.hpp"
|
||||
|
||||
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::dbms::SessionContext>,
|
||||
memgraph::dbms::SessionContext>;
|
||||
} // namespace memgraph::glue
|
||||
17
src/glue/ServerT.cpp
Normal file
17
src/glue/ServerT.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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/ServerT.hpp"
|
||||
|
||||
#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
|
||||
35
src/glue/ServerT.hpp
Normal file
35
src/glue/ServerT.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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 "communication/v2/server.hpp"
|
||||
#include "glue/SessionHL.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "dbms/session_context_handler.hpp"
|
||||
#else
|
||||
#include "dbms/session_context.hpp"
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
namespace memgraph::glue {
|
||||
#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
|
||||
386
src/glue/SessionHL.cpp
Normal file
386
src/glue/SessionHL.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
// 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/SessionHL.hpp"
|
||||
#include <optional>
|
||||
|
||||
#include "audit/log.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "glue/auth_checker.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
namespace memgraph::metrics {
|
||||
extern const Event ActiveBoltSessions;
|
||||
} // namespace memgraph::metrics
|
||||
|
||||
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
|
||||
auto const &as_map = extra.ValueMap();
|
||||
|
||||
auto metadata_pv = std::map<std::string, memgraph::storage::PropertyValue>{};
|
||||
|
||||
if (auto const it = as_map.find("tx_metadata"); it != as_map.cend() && it->second.IsMap()) {
|
||||
for (const auto &[key, bolt_md] : it->second.ValueMap()) {
|
||||
metadata_pv.emplace(key, memgraph::glue::ToPropertyValue(bolt_md));
|
||||
}
|
||||
}
|
||||
|
||||
auto tx_timeout = std::optional<int64_t>{};
|
||||
if (auto const it = as_map.find("tx_timeout"); it != as_map.cend() && it->second.IsInt()) {
|
||||
tx_timeout = it->second.ValueInt();
|
||||
}
|
||||
|
||||
return memgraph::query::QueryExtras{std::move(metadata_pv), tx_timeout};
|
||||
}
|
||||
|
||||
class TypedValueResultStreamBase {
|
||||
public:
|
||||
explicit TypedValueResultStreamBase(memgraph::query::InterpreterContext *interpreterContext);
|
||||
|
||||
std::vector<memgraph::communication::bolt::Value> DecodeValues(
|
||||
const std::vector<memgraph::query::TypedValue> &values) const;
|
||||
|
||||
private:
|
||||
// NOTE: Needed only for ToBoltValue conversions
|
||||
memgraph::query::InterpreterContext *interpreter_context_;
|
||||
};
|
||||
|
||||
/// Wrapper around TEncoder which converts TypedValue to Value
|
||||
/// before forwarding the calls to original TEncoder.
|
||||
template <typename TEncoder>
|
||||
class TypedValueResultStream : public TypedValueResultStreamBase {
|
||||
public:
|
||||
TypedValueResultStream(TEncoder *encoder, memgraph::query::InterpreterContext *ic)
|
||||
: TypedValueResultStreamBase{ic}, encoder_(encoder) {}
|
||||
|
||||
void Result(const std::vector<memgraph::query::TypedValue> &values) { encoder_->MessageRecord(DecodeValues(values)); }
|
||||
|
||||
private:
|
||||
TEncoder *encoder_;
|
||||
};
|
||||
|
||||
std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::DecodeValues(
|
||||
const std::vector<memgraph::query::TypedValue> &values) const {
|
||||
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, *interpreter_context_->db, memgraph::storage::View::NEW);
|
||||
if (maybe_value.HasError()) {
|
||||
switch (maybe_value.GetError()) {
|
||||
case memgraph::storage::Error::DELETED_OBJECT:
|
||||
throw memgraph::communication::bolt::ClientError("Returning a deleted object as a result.");
|
||||
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
||||
throw memgraph::communication::bolt::ClientError("Returning a nonexistent object as a result.");
|
||||
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
||||
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
||||
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
||||
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming results.");
|
||||
}
|
||||
}
|
||||
decoded_values.emplace_back(std::move(*maybe_value));
|
||||
}
|
||||
return decoded_values;
|
||||
}
|
||||
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::query::InterpreterContext *interpreterContext)
|
||||
: interpreter_context_(interpreterContext) {}
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
std::string SessionHL::GetDefaultDB() {
|
||||
if (user_.has_value()) {
|
||||
return user_->db_access().GetDefault();
|
||||
}
|
||||
return memgraph::dbms::kDefaultDB;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool SessionHL::Authenticate(const std::string &username, const std::string &password) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return user_.has_value();
|
||||
}
|
||||
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));
|
||||
} 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::map<std::string, memgraph::communication::bolt::Value> SessionHL::Pull(SessionHL::TEncoder *encoder,
|
||||
std::optional<int> n,
|
||||
std::optional<int> qid) {
|
||||
try {
|
||||
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) {
|
||||
std::map<std::string, memgraph::storage::PropertyValue> params_pv;
|
||||
for (const auto &[key, bolt_param] : params) {
|
||||
params_pv.emplace(key, ToPropertyValue(bolt_param));
|
||||
}
|
||||
const std::string *username{nullptr};
|
||||
if (user_) {
|
||||
username = &user_->username();
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
||||
memgraph::storage::PropertyValue(params_pv), interpreter_context_->db->id());
|
||||
}
|
||||
#endif
|
||||
try {
|
||||
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();
|
||||
if (db_name.empty()) {
|
||||
throw memgraph::communication::bolt::ClientError(
|
||||
"You are not authorized to execute this query! Please contact your database administrator.");
|
||||
}
|
||||
throw memgraph::communication::bolt::ClientError(
|
||||
"You are not authorized to execute this query on database \"{}\"! Please contact your database "
|
||||
"administrator.",
|
||||
db_name);
|
||||
}
|
||||
return {std::move(result.headers), result.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());
|
||||
} catch (const memgraph::query::ReplicationException &e) {
|
||||
throw memgraph::communication::bolt::ClientError(e.what());
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
void SessionHL::Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::string db;
|
||||
bool update = false;
|
||||
// Check if user explicitly defined the database to use
|
||||
if (run_time_info.contains("db")) {
|
||||
const auto &db_info = run_time_info.at("db");
|
||||
if (!db_info.IsString()) {
|
||||
throw memgraph::communication::bolt::ClientError("Malformed database name.");
|
||||
}
|
||||
db = db_info.ValueString();
|
||||
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
|
||||
db = GetDefaultDB();
|
||||
update = db != current_.interpreter_context()->db->id();
|
||||
in_explicit_db_ = false;
|
||||
}
|
||||
|
||||
// Check if the underlying database needs to be updated
|
||||
if (update) {
|
||||
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::metrics::DecrementCounter(memgraph::metrics::ActiveBoltSessions); }
|
||||
SessionHL::SessionHL(
|
||||
#ifdef MG_ENTERPRISE
|
||||
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),
|
||||
#ifdef MG_ENTERPRISE
|
||||
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
|
||||
endpoint_(endpoint),
|
||||
run_id_(current_.run_id()) {
|
||||
// Metrics update
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
|
||||
for (const auto &kv : summary) {
|
||||
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:
|
||||
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
||||
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
||||
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
||||
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
||||
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming summary.");
|
||||
}
|
||||
}
|
||||
decoded_summary.emplace(kv.first, std::move(*maybe_value));
|
||||
}
|
||||
// Add this memgraph instance run_id, received from telemetry
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
} // namespace memgraph::glue
|
||||
161
src/glue/SessionHL.hpp
Normal file
161
src/glue/SessionHL.hpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// 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 "communication/v2/server.hpp"
|
||||
#include "communication/v2/session.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(
|
||||
#ifdef MG_ENTERPRISE
|
||||
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;
|
||||
|
||||
SessionHL(const SessionHL &) = delete;
|
||||
SessionHL &operator=(const SessionHL &) = delete;
|
||||
SessionHL(SessionHL &&) = delete;
|
||||
SessionHL &operator=(SessionHL &&) = delete;
|
||||
|
||||
void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) override;
|
||||
|
||||
using TEncoder = memgraph::communication::bolt::Encoder<
|
||||
memgraph::communication::bolt::ChunkedEncoderBuffer<memgraph::communication::v2::OutputStream>>;
|
||||
|
||||
void BeginTransaction(const std::map<std::string, memgraph::communication::bolt::Value> &extra) override;
|
||||
|
||||
void CommitTransaction() override;
|
||||
|
||||
void RollbackTransaction() override;
|
||||
|
||||
std::pair<std::vector<std::string>, std::optional<int>> 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) override;
|
||||
|
||||
std::map<std::string, memgraph::communication::bolt::Value> Pull(TEncoder *encoder, std::optional<int> n,
|
||||
std::optional<int> qid) override;
|
||||
|
||||
std::map<std::string, memgraph::communication::bolt::Value> Discard(std::optional<int> n,
|
||||
std::optional<int> qid) override;
|
||||
|
||||
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 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::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::communication::v2::ServerEndpoint endpoint_;
|
||||
// NOTE: run_id should be const but that complicates code a lot.
|
||||
std::optional<std::string> run_id_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::glue
|
||||
@@ -62,6 +62,10 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
|
||||
return auth::Permission::STORAGE_MODE;
|
||||
case query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT:
|
||||
return auth::Permission::TRANSACTION_MANAGEMENT;
|
||||
case query::AuthQuery::Privilege::MULTI_DATABASE_EDIT:
|
||||
return auth::Permission::MULTI_DATABASE_EDIT;
|
||||
case query::AuthQuery::Privilege::MULTI_DATABASE_USE:
|
||||
return auth::Permission::MULTI_DATABASE_USE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ AuthChecker::AuthChecker(
|
||||
: auth_(auth) {}
|
||||
|
||||
bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) const {
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const {
|
||||
std::optional<memgraph::auth::User> maybe_user;
|
||||
{
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
@@ -83,7 +84,7 @@ bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
|
||||
}
|
||||
}
|
||||
|
||||
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges);
|
||||
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges, db_name);
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -93,22 +94,34 @@ std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGra
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
auto user = user_.Lock();
|
||||
if (username != user->username()) {
|
||||
auto maybe_user = auth_->ReadLock()->GetUser(username);
|
||||
if (!maybe_user) {
|
||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
}
|
||||
*user = std::move(*maybe_user);
|
||||
}
|
||||
|
||||
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(std::move(*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,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges) {
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) { // NOLINT
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!db_name.empty() && !user.db_access().Contains(db_name)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
const auto user_permissions = user.GetPermissions();
|
||||
return std::all_of(privileges.begin(), privileges.end(), [&user_permissions](const auto privilege) {
|
||||
return user_permissions.Has(memgraph::glue::PrivilegeToPermission(privilege)) ==
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
@@ -25,18 +26,23 @@ class AuthChecker : public query::AuthChecker {
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth);
|
||||
|
||||
bool IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges) const override;
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const override;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
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);
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name = "");
|
||||
|
||||
private:
|
||||
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
|
||||
mutable memgraph::utils::Synchronized<auth::User, memgraph::utils::SpinLock> user_; // cached user
|
||||
};
|
||||
#ifdef MG_ENTERPRISE
|
||||
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
||||
|
||||
16
src/glue/auth_global.hpp
Normal file
16
src/glue/auth_global.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "auth/models.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/constants.hpp"
|
||||
@@ -122,6 +123,29 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowRolePrivileges(
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
|
||||
const std::optional<memgraph::auth::User> &user) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &db = user->db_access();
|
||||
const auto &allows = db.GetAllowAll();
|
||||
const auto &grants = db.GetGrants();
|
||||
const auto &denies = db.GetDenies();
|
||||
|
||||
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
|
||||
if (allows) {
|
||||
res.emplace_back("*");
|
||||
} else {
|
||||
std::vector<memgraph::query::TypedValue> grants_vec(grants.cbegin(), grants.cend());
|
||||
res.emplace_back(std::move(grants_vec));
|
||||
}
|
||||
std::vector<memgraph::query::TypedValue> denies_vec(denies.cbegin(), denies.cend());
|
||||
res.emplace_back(std::move(denies_vec));
|
||||
return {res};
|
||||
}
|
||||
|
||||
std::vector<FineGrainedPermissionForPrivilegeResult> GetFineGrainedPermissionForPrivilegeForUserOrRole(
|
||||
const memgraph::auth::FineGrainedAccessPermissions &permissions, const std::string &permission_type,
|
||||
const std::string &user_or_role) {
|
||||
@@ -268,6 +292,10 @@ bool AuthQueryHandler::CreateUser(const std::string &username, const std::option
|
||||
}
|
||||
#endif
|
||||
);
|
||||
#ifdef MG_ENTERPRISE
|
||||
GrantDatabaseToUser(auth::kAllDatabases, username);
|
||||
SetMainDatabase(username, dbms::kDefaultDB);
|
||||
#endif
|
||||
}
|
||||
|
||||
return user_added;
|
||||
@@ -319,6 +347,67 @@ bool AuthQueryHandler::CreateRole(const std::string &rolename) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool AuthQueryHandler::RevokeDatabaseFromUser(const std::string &db, const std::string &username) {
|
||||
if (!std::regex_match(username, name_regex_)) {
|
||||
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
||||
}
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->RevokeDatabaseFromUser(db, username);
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthQueryHandler::GrantDatabaseToUser(const std::string &db, const std::string &username) {
|
||||
if (!std::regex_match(username, name_regex_)) {
|
||||
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
||||
}
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->GrantDatabaseToUser(db, username);
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
|
||||
const std::string &username) {
|
||||
if (!std::regex_match(username, name_regex_)) {
|
||||
throw memgraph::query::QueryRuntimeException("Invalid user or role name.");
|
||||
}
|
||||
try {
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
|
||||
}
|
||||
return ShowDatabasePrivileges(user);
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthQueryHandler::SetMainDatabase(const std::string &db, const std::string &username) {
|
||||
if (!std::regex_match(username, name_regex_)) {
|
||||
throw memgraph::query::QueryRuntimeException("Invalid user name.");
|
||||
}
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->SetMainDatabase(db, username);
|
||||
} catch (const memgraph::auth::AuthException &e) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AuthQueryHandler::DropRole(const std::string &rolename) {
|
||||
if (!std::regex_match(rolename, name_regex_)) {
|
||||
throw memgraph::query::QueryRuntimeException("Invalid role name.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <regex>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "auth_global.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
@@ -21,8 +22,6 @@
|
||||
|
||||
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_;
|
||||
@@ -38,6 +37,16 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
||||
|
||||
void SetPassword(const std::string &username, const std::optional<std::string> &password) override;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool RevokeDatabaseFromUser(const std::string &db, const std::string &username) override;
|
||||
|
||||
bool GrantDatabaseToUser(const std::string &db, const std::string &username) override;
|
||||
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) override;
|
||||
|
||||
bool SetMainDatabase(const std::string &db, const std::string &username) override;
|
||||
#endif
|
||||
|
||||
bool CreateRole(const std::string &rolename) override;
|
||||
|
||||
bool DropRole(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)
|
||||
const auto element_id = std::to_string(id.AsInt());
|
||||
return communication::bolt::Vertex{id, labels, properties, element_id};
|
||||
auto element_id = std::to_string(id.AsInt());
|
||||
return communication::bolt::Vertex{id, std::move(labels), std::move(properties), std::move(element_id)};
|
||||
}
|
||||
|
||||
storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccessor &edge, const storage::Storage &db,
|
||||
@@ -171,7 +171,8 @@ 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, type, properties, element_id, from_element_id, to_element_id};
|
||||
return communication::bolt::Edge{
|
||||
id, from, to, std::move(type), std::move(properties), element_id, from_element_id, to_element_id};
|
||||
}
|
||||
|
||||
storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, const storage::Storage &db,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -63,7 +63,7 @@ inline void LoadConfig(const std::string &product_name) {
|
||||
delete[] custom_argv;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> LoadUsernameAndPassword(const std::string &pass_file) {
|
||||
inline std::pair<std::string, std::string> LoadUsernameAndPassword(const std::string &pass_file) {
|
||||
std::ifstream file(pass_file);
|
||||
if (file.fail()) {
|
||||
spdlog::warn("Problem with opening MG_PASSFILE, memgraph server will start without user");
|
||||
|
||||
@@ -47,10 +47,10 @@ struct MetricsResponse {
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_histograms{};
|
||||
};
|
||||
|
||||
template <typename TSessionData>
|
||||
template <typename TSessionContext>
|
||||
class MetricsService {
|
||||
public:
|
||||
explicit MetricsService(TSessionData *data) : db_(data->interpreter_context->db.get()) {}
|
||||
explicit MetricsService(TSessionContext *session_context) : db_(session_context->interpreter_context->db.get()) {}
|
||||
|
||||
nlohmann::json GetMetricsJSON() {
|
||||
auto response = GetMetrics();
|
||||
@@ -101,6 +101,7 @@ class MetricsService {
|
||||
auto GetEventCounters() {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_counters{};
|
||||
event_counters.reserve(memgraph::metrics::CounterEnd());
|
||||
|
||||
for (auto i = 0; i < memgraph::metrics::CounterEnd(); i++) {
|
||||
event_counters.emplace_back(memgraph::metrics::GetCounterName(i), memgraph::metrics::GetCounterType(i),
|
||||
@@ -113,6 +114,7 @@ class MetricsService {
|
||||
auto GetEventGauges() {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
std::vector<std::tuple<std::string, std::string, uint64_t>> event_gauges{};
|
||||
event_gauges.reserve(memgraph::metrics::GaugeEnd());
|
||||
|
||||
for (auto i = 0; i < memgraph::metrics::GaugeEnd(); i++) {
|
||||
event_gauges.emplace_back(memgraph::metrics::GetGaugeName(i), memgraph::metrics::GetGaugeType(i),
|
||||
@@ -141,10 +143,10 @@ class MetricsService {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TSessionData>
|
||||
template <typename TSessionContext>
|
||||
class MetricsRequestHandler final {
|
||||
public:
|
||||
explicit MetricsRequestHandler(TSessionData *data) : service_(data) {
|
||||
explicit MetricsRequestHandler(TSessionContext *session_context) : service_(session_context) {
|
||||
spdlog::info("Basic request handler started!");
|
||||
}
|
||||
|
||||
@@ -206,6 +208,6 @@ class MetricsRequestHandler final {
|
||||
}
|
||||
|
||||
private:
|
||||
MetricsService<TSessionData> service_;
|
||||
MetricsService<TSessionContext> service_;
|
||||
};
|
||||
} // namespace memgraph::http
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -86,8 +86,8 @@ struct ConsumerInfo {
|
||||
std::string bootstrap_servers;
|
||||
std::chrono::milliseconds batch_interval;
|
||||
int64_t batch_size;
|
||||
std::unordered_map<std::string, std::string> public_configs;
|
||||
std::unordered_map<std::string, std::string> private_configs;
|
||||
std::unordered_map<std::string, std::string> public_configs{};
|
||||
std::unordered_map<std::string, std::string> private_configs{};
|
||||
};
|
||||
|
||||
/// Memgraphs Kafka consumer wrapper.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -91,7 +91,6 @@ std::string Endpoint::SocketAddress() const {
|
||||
return ip_address + ":" + std::to_string(port);
|
||||
}
|
||||
|
||||
Endpoint::Endpoint() {}
|
||||
Endpoint::Endpoint(std::string ip_address, uint16_t port) : address(std::move(ip_address)), port(port) {
|
||||
IpFamily ip_family = GetIpFamily(address);
|
||||
if (ip_family == IpFamily::NONE) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -25,8 +25,13 @@ namespace memgraph::io::network {
|
||||
* connection address.
|
||||
*/
|
||||
struct Endpoint {
|
||||
Endpoint();
|
||||
Endpoint() = default;
|
||||
Endpoint(std::string ip_address, uint16_t port);
|
||||
Endpoint(Endpoint const &) = default;
|
||||
Endpoint(Endpoint &&) noexcept = default;
|
||||
Endpoint &operator=(Endpoint const &) = default;
|
||||
Endpoint &operator=(Endpoint &&) noexcept = default;
|
||||
~Endpoint() = default;
|
||||
|
||||
enum class IpFamily : std::uint8_t { NONE, IP4, IP6 };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -36,7 +36,13 @@ KVStore::KVStore(std::filesystem::path storage) : pimpl_(std::make_unique<impl>(
|
||||
pimpl_->db.reset(db);
|
||||
}
|
||||
|
||||
KVStore::~KVStore() {}
|
||||
KVStore::~KVStore() {
|
||||
spdlog::debug("Destroying KVStore at {}", pimpl_->storage.string());
|
||||
const auto sync = pimpl_->db->SyncWAL();
|
||||
if (!sync.ok()) spdlog::error("KVStore sync failed!");
|
||||
const auto close = pimpl_->db->Close();
|
||||
if (!close.ok()) spdlog::error("KVStore close failed!");
|
||||
}
|
||||
|
||||
KVStore::KVStore(KVStore &&other) { pimpl_ = std::move(other.pimpl_); }
|
||||
|
||||
@@ -66,7 +72,7 @@ std::optional<std::string> KVStore::Get(const std::string &key) const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
bool KVStore::Delete(const std::string &key) {
|
||||
bool KVStore::Delete(std::string_view key) {
|
||||
auto s = pimpl_->db->Delete(rocksdb::WriteOptions(), key);
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@@ -91,7 +91,7 @@ class KVStore final {
|
||||
* true if the key doesn't exist and underlying storage
|
||||
* didn't encounter any error.
|
||||
*/
|
||||
bool Delete(const std::string &key);
|
||||
bool Delete(std::string_view key);
|
||||
|
||||
/**
|
||||
* Deletes the keys and corresponding values from storage.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user